Pg: Support custom type overrides with callbacks
This commit is contained in:
parent
327fd9ea50
commit
4686097d00
6 changed files with 227 additions and 85 deletions
76
c/pgconn.c
76
c/pgconn.c
|
|
@ -27,6 +27,7 @@ static void fupg_prep_destroy(fupg_prep *p) {
|
|||
|
||||
typedef struct {
|
||||
const fupg_type *send, *recv;
|
||||
SV *sendcb, *recvcb;
|
||||
} fupg_override;
|
||||
|
||||
#define fupg_name_hash(v) kh_hash_str((v).n)
|
||||
|
|
@ -209,13 +210,26 @@ 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->oidtypes, k) {
|
||||
SvREFCNT_dec(kh_val(c->oidtypes, k).sendcb);
|
||||
SvREFCNT_dec(kh_val(c->oidtypes, k).recvcb);
|
||||
}
|
||||
fupg_oid_overrides_destroy(c->oidtypes);
|
||||
|
||||
kh_foreach(c->nametypes, k) {
|
||||
SvREFCNT_dec(kh_val(c->nametypes, k).sendcb);
|
||||
SvREFCNT_dec(kh_val(c->nametypes, k).recvcb);
|
||||
}
|
||||
fupg_name_overrides_destroy(c->nametypes);
|
||||
|
||||
kh_foreach(c->records, k) safefree(kh_val(c->records, k));
|
||||
fupg_records_destroy(c->records);
|
||||
|
||||
kh_foreach(c->prep_map, k) fupg_prep_destroy(kh_key(c->prep_map, k));
|
||||
fupg_prepared_destroy(c->prep_map);
|
||||
|
||||
safefree(c);
|
||||
}
|
||||
|
||||
|
|
@ -367,9 +381,19 @@ static void fupg_prepared_unref(fupg_conn *c, fupg_prep *p) {
|
|||
|
||||
/* Type handling */
|
||||
|
||||
static const fupg_type *fupg_resolve_builtin(pTHX_ SV *name) {
|
||||
static const fupg_type *fupg_resolve_builtin(pTHX_ SV *name, SV **cb) {
|
||||
SvGETMAGIC(name);
|
||||
*cb = NULL;
|
||||
if (!SvOK(name)) return NULL;
|
||||
|
||||
if (SvROK(name)) {
|
||||
SV *rv = SvRV(name);
|
||||
if (SvTYPE(rv) == SVt_PVCV) {
|
||||
*cb = SvREFCNT_inc(name);
|
||||
return &fupg_type_perlcb;
|
||||
}
|
||||
}
|
||||
|
||||
UV uv;
|
||||
const char *pv = SvPV_nomg_nolen(name);
|
||||
const fupg_type *t = grok_atoUV(pv, &uv, NULL) && uv <= (UV)UINT_MAX
|
||||
|
|
@ -381,8 +405,8 @@ static const fupg_type *fupg_resolve_builtin(pTHX_ SV *name) {
|
|||
|
||||
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);
|
||||
o.send = fupg_resolve_builtin(sendsv, &o.sendcb);
|
||||
o.recv = fupg_resolve_builtin(recvsv, &o.recvcb);
|
||||
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
|
||||
|
|
@ -393,18 +417,24 @@ static void fupg_set_type(pTHX_ fupg_conn *c, SV *name, SV *sendsv, SV *recvsv)
|
|||
UV uv;
|
||||
STRLEN len;
|
||||
const char *pv = SvPV(name, len);
|
||||
int k, i;
|
||||
int k, absent;
|
||||
fupg_override *so = NULL;
|
||||
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;
|
||||
k = fupg_oid_overrides_put(c->oidtypes, (Oid)uv, &absent);
|
||||
so = &kh_val(c->oidtypes, k);
|
||||
} 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;
|
||||
k = fupg_name_overrides_put(c->nametypes, n, &absent);
|
||||
so = &kh_val(c->nametypes, k);
|
||||
} else {
|
||||
fu_confess("Invalid type oid or name '%s'", pv);
|
||||
}
|
||||
if (!absent) {
|
||||
SvREFCNT_dec(so->sendcb);
|
||||
SvREFCNT_dec(so->recvcb);
|
||||
}
|
||||
*so = o;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -517,19 +547,19 @@ 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) {
|
||||
static const fupg_type *fupg_override_get(fupg_conn *c, int flags, Oid oid, const fupg_name *name, SV **cb) {
|
||||
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
|
||||
fupg_override *o;
|
||||
if (name == NULL) {
|
||||
k = fupg_oid_overrides_get(c->oidtypes, oid);
|
||||
R(oidtypes);
|
||||
o = k == kh_end(c->oidtypes) ? NULL : &kh_val(c->oidtypes, k);
|
||||
} else {
|
||||
k = fupg_name_overrides_get(c->nametypes, *name);
|
||||
R(nametypes);
|
||||
o = k == kh_end(c->nametypes) ? NULL : &kh_val(c->nametypes, k);
|
||||
}
|
||||
#undef R
|
||||
return NULL;
|
||||
if (!o) return NULL;
|
||||
*cb = flags & FUPGT_SEND ? o->sendcb : o->recvcb;
|
||||
return flags & FUPGT_SEND ? o->send : o->recv;
|
||||
}
|
||||
|
||||
static void fupg_tio_setup(pTHX_ fupg_conn *conn, fupg_tio *tio, int flags, Oid oid, int *refresh_done) {
|
||||
|
|
@ -547,12 +577,13 @@ static void fupg_tio_setup(pTHX_ fupg_conn *conn, fupg_tio *tio, int flags, Oid
|
|||
* Some send/recv functions have slightly different behavior based on oid,
|
||||
* in those cases this behavior is useful. */
|
||||
|
||||
SV *cb = NULL;
|
||||
const fupg_type *e, *t;
|
||||
e = t = fupg_override_get(conn, flags, oid, NULL);
|
||||
e = t = fupg_override_get(conn, flags, oid, NULL, &cb);
|
||||
if (!t) t = fupg_lookup_type(aTHX_ conn, refresh_done, oid);
|
||||
if (!t) fu_confess("No type found with oid %u", oid);
|
||||
tio->name = t->name.n;
|
||||
if (!e && (e = fupg_override_get(conn, flags, 0, &t->name))) t = e;
|
||||
if (!e && (e = fupg_override_get(conn, flags, 0, &t->name, &cb))) 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);
|
||||
|
|
@ -565,9 +596,14 @@ static void fupg_tio_setup(pTHX_ fupg_conn *conn, fupg_tio *tio, int flags, Oid
|
|||
|
||||
tio->send = t->send;
|
||||
tio->recv = t->recv;
|
||||
if (flags & FUPGT_SEND ? tio->send == fupg_send_array : tio->recv == fupg_recv_array) {
|
||||
|
||||
if (flags & FUPGT_SEND ? tio->send == fupg_send_perlcb : tio->recv == fupg_recv_perlcb) {
|
||||
tio->cb = cb;
|
||||
|
||||
} else if (flags & FUPGT_SEND ? tio->send == fupg_send_array : tio->recv == fupg_recv_array) {
|
||||
tio->arrayelem = safecalloc(1, sizeof(*tio->arrayelem));
|
||||
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)", tio->name, t->oid, t->elemoid);
|
||||
|
|
|
|||
49
c/pgtypes.c
49
c/pgtypes.c
|
|
@ -32,6 +32,7 @@ struct fupg_tio {
|
|||
const fupg_record *info;
|
||||
fupg_tio *tio;
|
||||
} record;
|
||||
SV *cb;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -416,6 +417,52 @@ SENDFN(record) {
|
|||
}
|
||||
|
||||
|
||||
RECVFN(perlcb) {
|
||||
dSP;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
|
||||
PUSHMARK(SP);
|
||||
mXPUSHs(newSVpvn(buf, len));
|
||||
PUTBACK;
|
||||
call_sv(ctx->cb, G_SCALAR);
|
||||
SPAGAIN;
|
||||
|
||||
SV *ret = newSV(0);
|
||||
sv_setsv(ret, POPs);
|
||||
PUTBACK;
|
||||
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SENDFN(perlcb) {
|
||||
dSP;
|
||||
|
||||
ENTER;
|
||||
SAVETMPS;
|
||||
|
||||
PUSHMARK(SP);
|
||||
XPUSHs(val);
|
||||
|
||||
PUTBACK;
|
||||
call_sv(ctx->cb, G_SCALAR);
|
||||
SPAGAIN;
|
||||
|
||||
SV *ret = POPs;
|
||||
PUTBACK;
|
||||
|
||||
STRLEN len;
|
||||
const char *buf = SvPV(ret, len);
|
||||
fustr_write(out, buf, len);
|
||||
|
||||
FREETMPS;
|
||||
LEAVE;
|
||||
}
|
||||
|
||||
|
||||
RECVFN(inet) { /* Also works for cidr */
|
||||
char tmp[128];
|
||||
if (len < 8) RERR("input data too short");
|
||||
|
|
@ -726,6 +773,8 @@ static const fupg_type fupg_builtin[] = {
|
|||
|
||||
#define FUPG_BUILTIN (sizeof(fupg_builtin) / sizeof(fupg_type))
|
||||
|
||||
static const fupg_type fupg_type_perlcb = { 0, 0, {"$perl_cb"}, fupg_send_perlcb, fupg_recv_perlcb };
|
||||
|
||||
|
||||
static const fupg_type *fupg_type_byoid(const fupg_type *list, int len, Oid oid) {
|
||||
int i, b = 0, e = len-1;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue