pg: Add support for record/composite types
This is simply magical. \o/ Vendored in khashl.h. I wouldn't have used it if this were the only place where I'd need a custom hash table, but it should come in handy for other tasks as well, especially when I get to implementing an LRU for prepared statement caching. (Can all be done with Perl HV's, but they're less efficient and more cumbersome for these tasks)
This commit is contained in:
parent
33fe0d98a8
commit
87d99e412b
5 changed files with 642 additions and 4 deletions
71
c/pgtypes.c
71
c/pgtypes.c
|
|
@ -7,6 +7,15 @@ 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);
|
||||
|
||||
/* Record/composite type definition */
|
||||
typedef struct {
|
||||
int nattrs;
|
||||
struct {
|
||||
Oid oid;
|
||||
char name[64];
|
||||
} attrs[];
|
||||
} fupg_record;
|
||||
|
||||
/* Type I/O context */
|
||||
struct fupg_tio {
|
||||
Oid oid;
|
||||
|
|
@ -15,12 +24,16 @@ struct fupg_tio {
|
|||
fupg_recv_fn recv;
|
||||
union {
|
||||
fupg_tio *arrayelem;
|
||||
struct {
|
||||
const fupg_record *info;
|
||||
fupg_tio *tio;
|
||||
} record;
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Oid oid;
|
||||
Oid elemoid; /* For arrays & domain types */
|
||||
Oid elemoid; /* For arrays & domain types; relid for records */
|
||||
char name[64];
|
||||
fupg_send_fn send;
|
||||
fupg_recv_fn recv;
|
||||
|
|
@ -332,6 +345,62 @@ SENDFN(array) {
|
|||
#undef ARRAY_MAXDIM
|
||||
|
||||
|
||||
RECVFN(record) {
|
||||
if (len < 4) RERR("input data too short");
|
||||
I32 nfields = fu_frombeI(32, buf);
|
||||
if (nfields != ctx->record.info->nattrs) RERR("expected %d fields but got %d", ctx->record.info->nattrs, nfields);
|
||||
buf += 4; len -= 4;
|
||||
HV *hv = newHV();
|
||||
SV *sv = sv_2mortal(newRV_noinc((SV *)hv));
|
||||
I32 i;
|
||||
for (i=0; i<nfields; i++) {
|
||||
if (len < 8) RERR("input data too short");
|
||||
U32 oid = fu_frombeU(32, buf);
|
||||
if (oid != ctx->record.info->attrs[i].oid)
|
||||
RERR("expected field %d to be of type %u but got %u", i, ctx->record.info->attrs[i].oid, oid);
|
||||
I32 vlen = fu_frombeI(32, buf+4);
|
||||
SV *r = &PL_sv_undef;
|
||||
buf += 8; len -= 8;
|
||||
if (vlen > len) RERR("input data too short");
|
||||
if (vlen >= 0) {
|
||||
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);
|
||||
}
|
||||
return SvREFCNT_inc(sv);
|
||||
}
|
||||
|
||||
SENDFN(record) {
|
||||
if (!SvROK(val)) SERR("expected a hashref");
|
||||
SV *sv = SvRV(val);
|
||||
SvGETMAGIC(sv);
|
||||
if (SvTYPE(sv) != SVt_PVHV) SERR("expected a hashref");
|
||||
HV *hv = (HV *)sv;
|
||||
|
||||
fustr_writebeU(32, out, ctx->record.info->nattrs);
|
||||
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);
|
||||
if (!rsv || !*rsv) {
|
||||
fustr_writebeI(32, out, -1);
|
||||
continue;
|
||||
}
|
||||
sv = *rsv;
|
||||
SvGETMAGIC(sv);
|
||||
if (!SvOK(sv)) {
|
||||
fustr_writebeI(32, out, -1);
|
||||
continue;
|
||||
}
|
||||
size_t lenoff = fustr_len(out);
|
||||
fustr_write(out, "\0\0\0\0", 4);
|
||||
ctx->record.tio[i].send(ctx->record.tio+i, sv, out);
|
||||
fu_tobeU(32, fustr_start(out) + lenoff, fustr_len(out) - lenoff - 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RECVFN(inet) { /* Also works for cidr */
|
||||
char tmp[128];
|
||||
if (len < 8) RERR("input data too short");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue