diff --git a/c/pgconn.c b/c/pgconn.c index 4b84463..3a20b56 100644 --- a/c/pgconn.c +++ b/c/pgconn.c @@ -471,6 +471,7 @@ static void fupg_refresh_types(pTHX_ fupg_conn *c) { snprintf(t->name.n, sizeof(t->name.n), "%s", PQgetvalue(r, i, 1)); char typ = *PQgetvalue(r, i, 2); t->elemoid = fu_frombeU(32, PQgetvalue(r, i, 3)); + const fupg_type *builtin; if (t->elemoid) { if (typ == 'd') { /* domain */ @@ -487,13 +488,14 @@ static void fupg_refresh_types(pTHX_ fupg_conn *c) { /* enum, can use text send/recv */ t->send = fupg_send_text; t->recv = fupg_recv_text; + } else if ((builtin = fupg_builtin_byoid(t->oid))) { + t->send = builtin->send; + t->recv = builtin->recv; + } else if ((builtin = fupg_dynoid_byname(t->name.n))) { + t->send = builtin->send; + t->recv = builtin->recv; } else { - /* TODO: (multi)ranges, by-name lookup for dynamic-oid types */ - const fupg_type *builtin = fupg_builtin_byoid(t->oid); - if (builtin) { - t->send = builtin->send; - t->recv = builtin->recv; - } + /* TODO: (multi)ranges */ } } PQclear(r); diff --git a/c/pgtypes.c b/c/pgtypes.c index f9c5b00..6fb8835 100644 --- a/c/pgtypes.c +++ b/c/pgtypes.c @@ -638,6 +638,72 @@ SENDFN(time) { fustr_writebeI(64, out, SvNV(val) * 1000000); } + + +/* VNDB types */ + +const char vndbtag_alpha[] = "\0""abcdefghijklmnopqrstuvwxyz?????"; + +static I16 vndbtag_parse(char **str) { + I16 tag = 0; + if (**str >= 'a' && **str <= 'z') { + tag = (**str - 'a' + 1) << 10; + (*str)++; + if (**str >= 'a' && **str <= 'z') { + tag |= (**str - 'a' + 1) << 5; + (*str)++; + if (**str >= 'a' && **str <= 'z') { + tag |= **str - 'a' + 1; + (*str)++; + } + } + } + return tag; +} + +void vndbtag_fmt(I16 tag, char *out) { + out[0] = vndbtag_alpha[(tag >> 10) & 31]; + out[1] = vndbtag_alpha[(tag >> 5) & 31]; + out[2] = vndbtag_alpha[(tag >> 0) & 31]; + out[3] = 0; +} + +RECVFN(vndbtag) { + RLEN(2); + SV *r = newSV(4); + SvPOK_only(r); + vndbtag_fmt(fu_frombeI(16, buf), SvPVX(r)); + SvCUR_set(r, strlen(SvPVX(r))); + return r; +} + +SENDFN(vndbtag) { + char *t = SvPV_nolen(val); + I16 v = vndbtag_parse(&t); + if (*t) SERR("Invalid vndbtag: '%s'", SvPV_nolen(val)); + fustr_writebeI(16, out, v); +} + + +#define VNDBID2_MAXNUM (((I64)1<<48)-1) + +RECVFN(vndbid) { + RLEN(8); + I64 v = fu_frombeI(64, buf); + char tbuf[4]; + vndbtag_fmt(v >> 48, tbuf); + return newSVpvf("%s%"UVuf, tbuf, (UV)(v & VNDBID2_MAXNUM)); +} + +SENDFN(vndbid) { + char *ostr = SvPV_nolen(val), *str = ostr; + UV num; + I16 tag = vndbtag_parse(&str); + if (!grok_atoUV(str, &num, NULL) || num > VNDBID2_MAXNUM) SERR("invalid vndbid '%s'", ostr); + fustr_writebeI(64, out, ((I64)tag)<<48 | num); +} + + #undef SIV #undef RLEN #undef RECVFN @@ -818,7 +884,24 @@ static const fupg_type fupg_builtin[] = { #define FUPG_BUILTIN (sizeof(fupg_builtin) / sizeof(fupg_type)) +/* List of types identified by name */ + +#define DYNOID\ + T("vndbtag", vndbtag)\ + T("vndbid", vndbid) + +static const fupg_type fupg_dynoid[] = { +#define T(name, fun) { 0, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun }, + DYNOID +#undef T +}; + +#undef DYNOID +#define FUPG_DYNOID (sizeof(fupg_dynoid) / sizeof(fupg_type)) + + /* List of special types for use with set_type() */ + #define SPECIALS\ T("$date_str", date_str)\ T("$hex", hex ) @@ -851,8 +934,19 @@ static const fupg_type *fupg_builtin_byoid(Oid oid) { return fupg_type_byoid(fupg_builtin, FUPG_BUILTIN, oid); } +static const fupg_type *fupg_dynoid_byname(const char *name) { + size_t i; + for (i=0; iq("SELECT dom FROM fupg_test_table")->val, 0x6262; }; + +subtest 'vndbid', sub { + plan skip_all => 'type not loaded in the database' if !$conn->q("SELECT 1 FROM pg_type WHERE typname = 'vndbtag'")->val; + + for my $t (qw/a zz xxx/) { + is $conn->q('SELECT $1::vndbtag', $t)->val, $t; + is $conn->q('SELECT $1::vndbtag', $t)->text_params->val, $t; + is $conn->q('SELECT $1::vndbtag', $t)->text_results->val, $t; + } + ok !eval { $conn->q('SELECT $1::vndbtag', '')->val }; + ok !eval { $conn->q('SELECT $1::vndbtag', 'abcd')->val }; + + for my $t (qw/a123 zz992883231 xxx18388123/) { + is $conn->q('SELECT $1::vndbid', $t)->val, $t; + is $conn->q('SELECT $1::vndbid', $t)->text_params->val, $t; + is $conn->q('SELECT $1::vndbid', $t)->text_results->val, $t; + } + ok !eval { $conn->q('SELECT $1::vndbid', '')->val }; + ok !eval { $conn->q('SELECT $1::vndbid', 'ab')->val }; + ok !eval { $conn->q('SELECT $1::vndbid', 'ab1219229999999999')->val }; +}; + done_testing;