Pg: Add "$hex" pseudo-type
This commit is contained in:
parent
baf0f90bd5
commit
15954f4ad5
6 changed files with 63 additions and 15 deletions
1
FU.xs
1
FU.xs
|
|
@ -236,6 +236,7 @@ void q(fupg_conn *c, SV *sv, ...)
|
||||||
void _set_type(fupg_conn *c, SV *name, SV *sendsv, SV *recvsv)
|
void _set_type(fupg_conn *c, SV *name, SV *sendsv, SV *recvsv)
|
||||||
CODE:
|
CODE:
|
||||||
fupg_set_type(c, name, sendsv, recvsv);
|
fupg_set_type(c, name, sendsv, recvsv);
|
||||||
|
XSRETURN(1);
|
||||||
|
|
||||||
|
|
||||||
MODULE = FU PACKAGE = FU::Pg::txn
|
MODULE = FU PACKAGE = FU::Pg::txn
|
||||||
|
|
|
||||||
18
FU/Pg.pm
18
FU/Pg.pm
|
|
@ -619,7 +619,21 @@ supported. C<undef> always converts to SQL C<NULL>.
|
||||||
=item bytea
|
=item bytea
|
||||||
|
|
||||||
The C<bytea> type represents arbitrary binary data and this module will pass
|
The C<bytea> type represents arbitrary binary data and this module will pass
|
||||||
that along as raw binary strings.
|
that along as raw binary strings. If you prefer to work with hex strings
|
||||||
|
instead, use:
|
||||||
|
|
||||||
|
$conn->set_type(bytea => '$hex');
|
||||||
|
|
||||||
|
The I<bytea> and the I<$hex> (pseudo-)types can be applied to any other type to
|
||||||
|
convert between the PostgreSQL binary wire format and Perl strings. For
|
||||||
|
example, if you prefer to receive integers as big-endian hex strings, you can
|
||||||
|
do that:
|
||||||
|
|
||||||
|
$conn->set_type(int4 => recv => '$hex');
|
||||||
|
|
||||||
|
Or to treat UUIDs as 16-byte strings:
|
||||||
|
|
||||||
|
$conn->set_type(uuid => 'bytea');
|
||||||
|
|
||||||
=item timestamp / timestamptz
|
=item timestamp / timestamptz
|
||||||
|
|
||||||
|
|
@ -724,8 +738,6 @@ C<set_type()> to configure appropriate conversions for these types.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
I<TODO:> Some handy special types for overriding common conversions.
|
|
||||||
|
|
||||||
I<TODO:> Methods to convert between the various formats.
|
I<TODO:> Methods to convert between the various formats.
|
||||||
|
|
||||||
I<TODO:> Methods to query type info.
|
I<TODO:> Methods to query type info.
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,11 @@ static double fu_timediff(const struct timespec *a, const struct timespec *b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int fu_hexdig(char x) {
|
||||||
|
return x >= '0' && x <= '9' ? x-'0' : x >= 'A' && x <= 'F' ? x-'A'+10 : x >= 'a' && x <= 'f' ? x-'a'+10 : 0x10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* -1 if arg is not a bool, 0 on false, 1 on true */
|
/* -1 if arg is not a bool, 0 on false, 1 on true */
|
||||||
static int fu_2bool(pTHX_ SV *val) {
|
static int fu_2bool(pTHX_ SV *val) {
|
||||||
|
|
|
||||||
|
|
@ -30,26 +30,22 @@ static inline int fujson_parse_string_escape(pTHX_ fujson_parse_ctx *ctx, fustr
|
||||||
case 'r': *(r->cur++) = 0x0d; break;
|
case 'r': *(r->cur++) = 0x0d; break;
|
||||||
case 'u':
|
case 'u':
|
||||||
/* (awful code adapted from ncdu) */
|
/* (awful code adapted from ncdu) */
|
||||||
#define INV (1<<16)
|
#define h4(b) (fu_hexdig((b)[0])<<12) + (fu_hexdig((b)[1])<<8) + (fu_hexdig((b)[2])<<4) + fu_hexdig((b)[3])
|
||||||
#define hn(n) (n >= '0' && n <= '9' ? n-'0' : n >= 'A' && n <= 'F' ? n-'A'+10 : n >= 'a' && n <= 'f' ? n-'a'+10 : INV)
|
|
||||||
#define h4(b) (hn((b)[0])<<12) + (hn((b)[1])<<8) + (hn((b)[2])<<4) + hn((b)[3])
|
|
||||||
if (ctx->end - ctx->buf < 4) return 1;
|
if (ctx->end - ctx->buf < 4) return 1;
|
||||||
n = h4(ctx->buf);
|
n = h4(ctx->buf);
|
||||||
if (n >= INV || (n & 0xfc00) == 0xdc00) return 1;
|
if (n >= 0x10000 || (n & 0xfc00) == 0xdc00) return 1;
|
||||||
ctx->buf += 4;
|
ctx->buf += 4;
|
||||||
if ((n & 0xfc00) == 0xd800) { /* high surrogate */
|
if ((n & 0xfc00) == 0xd800) { /* high surrogate */
|
||||||
if (ctx->end - ctx->buf < 6) return 1;
|
if (ctx->end - ctx->buf < 6) return 1;
|
||||||
if (ctx->buf[0] != '\\' || ctx->buf[1] != 'u') return 1;
|
if (ctx->buf[0] != '\\' || ctx->buf[1] != 'u') return 1;
|
||||||
s = h4(ctx->buf+2);
|
s = h4(ctx->buf+2);
|
||||||
if (s >= INV || (s & 0xfc00) != 0xdc00) return 1;
|
if (s >= 0x10000 || (s & 0xfc00) != 0xdc00) return 1;
|
||||||
n = 0x10000 + (((n & 0x03ff) << 10) | (s & 0x03ff));
|
n = 0x10000 + (((n & 0x03ff) << 10) | (s & 0x03ff));
|
||||||
ctx->buf += 6;
|
ctx->buf += 6;
|
||||||
}
|
}
|
||||||
r->cur = (char *)uvchr_to_utf8((U8 *)r->cur, n);
|
r->cur = (char *)uvchr_to_utf8((U8 *)r->cur, n);
|
||||||
if (n >= 0x80) r->setutf8 = 1;
|
if (n >= 0x80) r->setutf8 = 1;
|
||||||
break;
|
break;
|
||||||
#undef INV
|
|
||||||
#undef hn
|
|
||||||
#undef h4
|
#undef h4
|
||||||
default:
|
default:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
37
c/pgtypes.c
37
c/pgtypes.c
|
|
@ -156,6 +156,33 @@ SENDFN(bytea) {
|
||||||
fustr_write(out, buf, len);
|
fustr_write(out, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RECVFN(hex) {
|
||||||
|
SV *r = newSV(len ? len * 2 : 1);
|
||||||
|
SvPOK_only(r);
|
||||||
|
char *out = SvPVX(r);
|
||||||
|
const unsigned char *in = (const unsigned char *)buf;
|
||||||
|
int i;
|
||||||
|
for (i=0; i<len; i++) {
|
||||||
|
*out++ = PL_hexdigit[(in[i] >> 4) & 0x0f];
|
||||||
|
*out++ = PL_hexdigit[in[i] & 0x0f];
|
||||||
|
}
|
||||||
|
SvCUR_set(r, len * 2);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
SENDFN(hex) {
|
||||||
|
STRLEN len;
|
||||||
|
const char *in = SvPV(val, len);
|
||||||
|
const char *end = in + len;
|
||||||
|
if (len % 2) SERR("Invalid hex string");
|
||||||
|
while (in < end) {
|
||||||
|
int v = (fu_hexdig(*in)<<4) + fu_hexdig(in[1]);
|
||||||
|
if (v > 0xff) SERR("Invalid hex string");
|
||||||
|
fustr_write_ch(out, v);
|
||||||
|
in += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RECVFN(char) {
|
RECVFN(char) {
|
||||||
RLEN(1);
|
RLEN(1);
|
||||||
return newSVpvn(buf, len);
|
return newSVpvn(buf, len);
|
||||||
|
|
@ -515,7 +542,7 @@ RECVFN(uuid) {
|
||||||
RLEN(16);
|
RLEN(16);
|
||||||
char tmp[64];
|
char tmp[64];
|
||||||
char *out = tmp;
|
char *out = tmp;
|
||||||
unsigned char *in = (unsigned char *)buf;
|
const unsigned char *in = (const unsigned char *)buf;
|
||||||
int i;
|
int i;
|
||||||
for (i=0; i<16; i++) {
|
for (i=0; i<16; i++) {
|
||||||
if (i == 4 || i == 6 || i == 8 || i == 10) *out++ = '-';
|
if (i == 4 || i == 6 || i == 8 || i == 10) *out++ = '-';
|
||||||
|
|
@ -534,9 +561,8 @@ SENDFN(uuid) {
|
||||||
for (; *in; in++) {
|
for (; *in; in++) {
|
||||||
if (*in == '}') break;
|
if (*in == '}') break;
|
||||||
if (dig == 0x10 && *in == '-') continue;
|
if (dig == 0x10 && *in == '-') continue;
|
||||||
unsigned char x = *in;
|
int x = fu_hexdig(*in);
|
||||||
x = x >= '0' && x <= '9' ? x-'0' : x >= 'A' && x <= 'F' ? x-'A'+10 : x >= 'a' && x <= 'f' ? x-'a'+10 : 0x10;
|
if (x > 0x10) SERR("invalid UUID");
|
||||||
if (x == 0x10) SERR("invalid UUID");
|
|
||||||
if (bytes >= 16) SERR("invalid UUID");
|
if (bytes >= 16) SERR("invalid UUID");
|
||||||
if (dig == 0x10) dig = x;
|
if (dig == 0x10) dig = x;
|
||||||
else {
|
else {
|
||||||
|
|
@ -794,7 +820,8 @@ static const fupg_type fupg_builtin[] = {
|
||||||
|
|
||||||
/* List of special types for use with set_type() */
|
/* List of special types for use with set_type() */
|
||||||
#define SPECIALS\
|
#define SPECIALS\
|
||||||
T("$date_str", date_str)
|
T("$date_str", date_str)\
|
||||||
|
T("$hex", hex )
|
||||||
|
|
||||||
static const fupg_type fupg_specials[] = {
|
static const fupg_type fupg_specials[] = {
|
||||||
#define T(name, fun) { 0, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun },
|
#define T(name, fun) { 0, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun },
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,13 @@ v bytea => 'hello', undef, '\x68656c6c6f';
|
||||||
v bytea => "\xaf\x90", undef, '\xaf90';
|
v bytea => "\xaf\x90", undef, '\xaf90';
|
||||||
f bytea => "\x{1234}";
|
f bytea => "\x{1234}";
|
||||||
|
|
||||||
|
$conn->set_type(bytea => '$hex');
|
||||||
|
v bytea => '', undef, '\x';
|
||||||
|
v bytea => '68656c6c6f', undef, '\x68656c6c6f';
|
||||||
|
v bytea => "af90", undef, '\xaf90';
|
||||||
|
f bytea => 'a';
|
||||||
|
f bytea => 'a f';
|
||||||
|
|
||||||
v '"char"' => $_ for (1, '1', 'a', 'A', '-');
|
v '"char"' => $_ for (1, '1', 'a', 'A', '-');
|
||||||
v '"char"' => "\x84", undef, '\204';
|
v '"char"' => "\x84", undef, '\204';
|
||||||
f '"char"' => $_ for ('', 'ab', "\x{1234}");
|
f '"char"' => $_ for ('', 'ab', "\x{1234}");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue