Pg: Support custom type overrides with callbacks

This commit is contained in:
Yorhel 2025-02-28 11:23:37 +01:00
parent 327fd9ea50
commit 4686097d00
6 changed files with 227 additions and 85 deletions

View file

@ -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);

View file

@ -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;