Pg: Support type override configuration
This commit is contained in:
parent
3662931fc2
commit
327fd9ea50
5 changed files with 215 additions and 34 deletions
96
c/pgconn.c
96
c/pgconn.c
|
|
@ -25,6 +25,15 @@ static void fupg_prep_destroy(fupg_prep *p) {
|
|||
safefree(p);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const fupg_type *send, *recv;
|
||||
} fupg_override;
|
||||
|
||||
#define fupg_name_hash(v) kh_hash_str((v).n)
|
||||
#define fupg_name_eq(a,b) kh_eq_str((a).n, (b).n)
|
||||
KHASHL_MAP_INIT(KH_LOCAL, fupg_oid_overrides, fupg_oid_overrides, Oid, fupg_override, kh_hash_uint32, kh_eq_generic);
|
||||
KHASHL_MAP_INIT(KH_LOCAL, fupg_name_overrides, fupg_name_overrides, fupg_name, fupg_override, fupg_name_hash, fupg_name_eq);
|
||||
|
||||
|
||||
typedef struct {
|
||||
SV *self;
|
||||
|
|
@ -38,6 +47,8 @@ typedef struct {
|
|||
unsigned int prep_max;
|
||||
unsigned int prep_cur; /* Number of prepared statements not associated with an active $st object */
|
||||
fupg_type *types;
|
||||
fupg_oid_overrides *oidtypes;
|
||||
fupg_name_overrides *nametypes;
|
||||
fupg_records *records;
|
||||
fupg_prepared *prep_map;
|
||||
fupg_prep *prep_head, *prep_tail; /* Inserted into head, removed at tail */
|
||||
|
|
@ -166,6 +177,8 @@ static SV *fupg_connect(pTHX_ const char *str) {
|
|||
c->ntypes = 0;
|
||||
c->types = NULL;
|
||||
c->records = fupg_records_init();
|
||||
c->oidtypes = fupg_oid_overrides_init();
|
||||
c->nametypes = fupg_name_overrides_init();
|
||||
c->prep_cur = 0;
|
||||
c->prep_max = 256;
|
||||
c->prep_map = fupg_prepared_init();
|
||||
|
|
@ -196,6 +209,8 @@ static void fupg_conn_destroy(pTHX_ fupg_conn *c) {
|
|||
PQfinish(c->conn);
|
||||
if (c->buf.sv) SvREFCNT_dec(c->buf.sv);
|
||||
safefree(c->types);
|
||||
fupg_oid_overrides_destroy(c->oidtypes);
|
||||
fupg_name_overrides_destroy(c->nametypes);
|
||||
khint_t k;
|
||||
kh_foreach(c->records, k) safefree(kh_val(c->records, k));
|
||||
fupg_records_destroy(c->records);
|
||||
|
|
@ -352,6 +367,47 @@ static void fupg_prepared_unref(fupg_conn *c, fupg_prep *p) {
|
|||
|
||||
/* Type handling */
|
||||
|
||||
static const fupg_type *fupg_resolve_builtin(pTHX_ SV *name) {
|
||||
SvGETMAGIC(name);
|
||||
if (!SvOK(name)) return NULL;
|
||||
UV uv;
|
||||
const char *pv = SvPV_nomg_nolen(name);
|
||||
const fupg_type *t = grok_atoUV(pv, &uv, NULL) && uv <= (UV)UINT_MAX
|
||||
? fupg_builtin_byoid((Oid)uv)
|
||||
: fupg_builtin_byname(pv);
|
||||
if (!t) fu_confess("No builtin type found with oid or name '%s'", pv);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void fupg_set_type(pTHX_ fupg_conn *c, SV *name, SV *sendsv, SV *recvsv) {
|
||||
fupg_override o;
|
||||
o.send = fupg_resolve_builtin(sendsv);
|
||||
o.recv = fupg_resolve_builtin(recvsv);
|
||||
if ((o.send && o.send->send == fupg_send_array) || (o.recv && o.recv->recv == fupg_recv_array))
|
||||
fu_confess("Cannot set a type to array, override the underlying element type instead");
|
||||
/* Can't currently happen since we have no records in the builtin type
|
||||
* list, but catch this just in case that changes. */
|
||||
if ((o.send && o.send->send == fupg_send_record) || (o.recv && o.recv->recv == fupg_recv_record))
|
||||
fu_confess("Cannot set a type to record");
|
||||
|
||||
UV uv;
|
||||
STRLEN len;
|
||||
const char *pv = SvPV(name, len);
|
||||
int k, i;
|
||||
if (grok_atoUV(pv, &uv, NULL) && uv <= (UV)UINT_MAX) {
|
||||
k = fupg_oid_overrides_put(c->oidtypes, (Oid)uv, &i);
|
||||
kh_val(c->oidtypes, k) = o;
|
||||
} else if (len < sizeof(fupg_name)) {
|
||||
fupg_name n;
|
||||
strcpy(n.n, pv);
|
||||
k = fupg_name_overrides_put(c->nametypes, n, &i);
|
||||
kh_val(c->nametypes, k) = o;
|
||||
} else {
|
||||
fu_confess("Invalid type oid or name '%s'", pv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* XXX: It feels a bit wasteful to load *all* types; even on an empty database
|
||||
* that's ~55k of data, but it's easier and (potentially) faster than fetching
|
||||
* each type seperately as we encounter them.
|
||||
|
|
@ -382,7 +438,7 @@ static void fupg_refresh_types(pTHX_ fupg_conn *c) {
|
|||
for (i=0; i<c->ntypes; i++) {
|
||||
fupg_type *t = c->types + i;
|
||||
t->oid = fu_frombeU(32, PQgetvalue(r, i, 0));
|
||||
snprintf(t->name, sizeof(t->name), "%s", PQgetvalue(r, i, 1));
|
||||
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));
|
||||
|
||||
|
|
@ -448,7 +504,7 @@ static const fupg_record *fupg_lookup_record(fupg_conn *c, Oid oid) {
|
|||
int i;
|
||||
for (i=0; i<record->nattrs; i++) {
|
||||
record->attrs[i].oid = fu_frombeU(32, PQgetvalue(r, i, 0));
|
||||
snprintf(record->attrs[i].name, sizeof(record->attrs->name), "%s", PQgetvalue(r, i, 1));
|
||||
snprintf(record->attrs[i].name.n, sizeof(record->attrs->name.n), "%s", PQgetvalue(r, i, 1));
|
||||
}
|
||||
k = fupg_records_put(c->records, oid, &i);
|
||||
kh_val(c->records, k) = record;
|
||||
|
|
@ -461,6 +517,21 @@ static const fupg_record *fupg_lookup_record(fupg_conn *c, Oid oid) {
|
|||
#define FUPGT_SEND 2
|
||||
#define FUPGT_RECV 4
|
||||
|
||||
static const fupg_type *fupg_override_get(fupg_conn *c, int flags, Oid oid, const fupg_name *name) {
|
||||
khint_t k;
|
||||
|
||||
#define R(t) if (k != kh_end(c->t)) return flags & FUPGT_SEND ? kh_val(c->t, k).send : kh_val(c->t, k).recv
|
||||
if (name == NULL) {
|
||||
k = fupg_oid_overrides_get(c->oidtypes, oid);
|
||||
R(oidtypes);
|
||||
} else {
|
||||
k = fupg_name_overrides_get(c->nametypes, *name);
|
||||
R(nametypes);
|
||||
}
|
||||
#undef R
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fupg_tio_setup(pTHX_ fupg_conn *conn, fupg_tio *tio, int flags, Oid oid, int *refresh_done) {
|
||||
tio->oid = oid;
|
||||
if (flags & FUPGT_TEXT) {
|
||||
|
|
@ -470,14 +541,25 @@ static void fupg_tio_setup(pTHX_ fupg_conn *conn, fupg_tio *tio, int flags, Oid
|
|||
return;
|
||||
}
|
||||
|
||||
const fupg_type *e, *t = fupg_lookup_type(aTHX_ conn, refresh_done, oid);
|
||||
/* Minor wart? When the type is overridden by oid, the name & oid in error
|
||||
* messages will be that of the builtin type. When overridden by name, the
|
||||
* name will be correct but the oid is still of the builtin type.
|
||||
* Some send/recv functions have slightly different behavior based on oid,
|
||||
* in those cases this behavior is useful. */
|
||||
|
||||
const fupg_type *e, *t;
|
||||
e = t = fupg_override_get(conn, flags, oid, NULL);
|
||||
if (!t) t = fupg_lookup_type(aTHX_ conn, refresh_done, oid);
|
||||
if (!t) fu_confess("No type found with oid %u", oid);
|
||||
if (!t->send || !t->recv) fu_confess("Unable to send or receive type '%s' (oid %u)", t->name, oid);
|
||||
tio->name = t->name;
|
||||
tio->name = t->name.n;
|
||||
if (!e && (e = fupg_override_get(conn, flags, 0, &t->name))) t = e;
|
||||
|
||||
if (flags & FUPGT_SEND && !t->send) fu_confess("Unable to send type '%s' (oid %u)", tio->name, oid);
|
||||
if (flags & FUPGT_RECV && !t->recv) fu_confess("Unable to receive type '%s' (oid %u)", tio->name, oid);
|
||||
|
||||
if (flags & FUPGT_SEND ? t->send == fupg_send_domain : t->recv == fupg_recv_domain) {
|
||||
e = fupg_lookup_type(aTHX_ conn, refresh_done, t->elemoid);
|
||||
if (!e) fu_confess("Base type %u not found for domain '%s' (oid %u)", t->elemoid, t->name, t->oid);
|
||||
if (!e) fu_confess("Base type %u not found for domain '%s' (oid %u)", t->elemoid, tio->name, t->oid);
|
||||
t = e;
|
||||
}
|
||||
|
||||
|
|
@ -488,7 +570,7 @@ static void fupg_tio_setup(pTHX_ fupg_conn *conn, fupg_tio *tio, int flags, Oid
|
|||
fupg_tio_setup(aTHX_ conn, tio->arrayelem, flags, t->elemoid, refresh_done);
|
||||
} else if (flags & FUPGT_SEND ? tio->send == fupg_send_record : tio->recv == fupg_recv_record) {
|
||||
tio->record.info = fupg_lookup_record(conn, t->elemoid);
|
||||
if (!tio->record.info) fu_confess("Unable to find attributes for record type '%s' (oid %u, relid %u)", t->name, t->oid, t->elemoid);
|
||||
if (!tio->record.info) fu_confess("Unable to find attributes for record type '%s' (oid %u, relid %u)", tio->name, t->oid, t->elemoid);
|
||||
tio->record.tio = safecalloc(tio->record.info->nattrs, sizeof(*tio->record.tio));
|
||||
int i;
|
||||
for (i=0; i<tio->record.info->nattrs; i++)
|
||||
|
|
|
|||
24
c/pgtypes.c
24
c/pgtypes.c
|
|
@ -7,12 +7,16 @@ typedef void (*fupg_send_fn)(pTHX_ const fupg_tio *, SV *, fustr *);
|
|||
/* Receive function, takes a binary string and should return a Perl value. */
|
||||
typedef SV *(*fupg_recv_fn)(pTHX_ const fupg_tio *, const char *, int);
|
||||
|
||||
typedef struct {
|
||||
char n[64];
|
||||
} fupg_name;
|
||||
|
||||
/* Record/composite type definition */
|
||||
typedef struct {
|
||||
int nattrs;
|
||||
struct {
|
||||
Oid oid;
|
||||
char name[64];
|
||||
fupg_name name;
|
||||
} attrs[];
|
||||
} fupg_record;
|
||||
|
||||
|
|
@ -34,7 +38,7 @@ struct fupg_tio {
|
|||
typedef struct {
|
||||
Oid oid;
|
||||
Oid elemoid; /* For arrays & domain types; relid for records */
|
||||
char name[64];
|
||||
fupg_name name;
|
||||
fupg_send_fn send;
|
||||
fupg_recv_fn recv;
|
||||
} fupg_type;
|
||||
|
|
@ -377,7 +381,7 @@ RECVFN(record) {
|
|||
r = ctx->record.tio[i].recv(aTHX_ ctx->record.tio+i, buf, vlen);
|
||||
buf += vlen; len -= vlen;
|
||||
}
|
||||
hv_store(hv, ctx->record.info->attrs[i].name, -strlen(ctx->record.info->attrs[i].name), r, 0);
|
||||
hv_store(hv, ctx->record.info->attrs[i].name.n, -strlen(ctx->record.info->attrs[i].name.n), r, 0);
|
||||
}
|
||||
return SvREFCNT_inc(sv);
|
||||
}
|
||||
|
|
@ -393,7 +397,7 @@ SENDFN(record) {
|
|||
I32 i;
|
||||
for (i=0; i<ctx->record.info->nattrs; i++) {
|
||||
fustr_writebeI(32, out, ctx->record.info->attrs[i].oid);
|
||||
SV **rsv = hv_fetch(hv, ctx->record.info->attrs[i].name, -strlen(ctx->record.info->attrs[i].name), 0);
|
||||
SV **rsv = hv_fetch(hv, ctx->record.info->attrs[i].name.n, -strlen(ctx->record.info->attrs[i].name.n), 0);
|
||||
if (!rsv || !*rsv) {
|
||||
fustr_writebeI(32, out, -1);
|
||||
continue;
|
||||
|
|
@ -711,8 +715,8 @@ SENDFN(date) {
|
|||
B( 5069, "xid8", uint8 )
|
||||
|
||||
static const fupg_type fupg_builtin[] = {
|
||||
#define B(oid, name, fun) { oid, 0, name"\0", fupg_send_##fun, fupg_recv_##fun },
|
||||
#define A(oid, name, eoid) { oid, eoid, name"\0", fupg_send_array, fupg_recv_array },
|
||||
#define B(oid, name, fun) { oid, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun },
|
||||
#define A(oid, name, eoid) { oid, eoid, {name"\0"}, fupg_send_array, fupg_recv_array },
|
||||
BUILTINS
|
||||
#undef B
|
||||
#undef A
|
||||
|
|
@ -737,3 +741,11 @@ static const fupg_type *fupg_type_byoid(const fupg_type *list, int len, Oid oid)
|
|||
static const fupg_type *fupg_builtin_byoid(Oid oid) {
|
||||
return fupg_type_byoid(fupg_builtin, FUPG_BUILTIN, oid);
|
||||
}
|
||||
|
||||
static const fupg_type *fupg_builtin_byname(const char *name) {
|
||||
size_t i;
|
||||
for (i=0; i<FUPG_BUILTIN; i++)
|
||||
if (strcmp(fupg_builtin[i].name.n, name) == 0)
|
||||
return fupg_builtin+i;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue