pg: Support array types
That wasn't quite as painful as I had anticipated. \o/
This commit is contained in:
parent
b6517cf05a
commit
7d71e446d0
4 changed files with 323 additions and 48 deletions
102
c/pgconn.c
102
c/pgconn.c
|
|
@ -270,31 +270,40 @@ static void fupg_refresh_types(pTHX_ fupg_conn *c) {
|
|||
c->types = 0;
|
||||
c->ntypes = 0;
|
||||
|
||||
const char *sql = "SELECT oid, typname, typtype FROM pg_type ORDER BY oid";
|
||||
const char *sql =
|
||||
"SELECT oid, typname, typtype"
|
||||
", CASE WHEN typcategory = 'A' THEN typelem ELSE 0 END"
|
||||
" FROM pg_type"
|
||||
" ORDER BY oid";
|
||||
PGresult *r = PQexecParams(c->conn, sql, 0, NULL, NULL, NULL, NULL, 1);
|
||||
if (!r) fupg_conn_croak(c, "exec");
|
||||
if (PQresultStatus(r) != PGRES_TUPLES_OK) fupg_result_croak(r, "exec", sql);
|
||||
|
||||
c->ntypes = PQntuples(r);
|
||||
c->types = calloc(c->ntypes, sizeof(*c->types));
|
||||
c->types = safecalloc(c->ntypes, sizeof(*c->types));
|
||||
int i;
|
||||
for (i=0; i<c->ntypes; i++) {
|
||||
fupg_type *t = c->types + i;
|
||||
t->oid = __builtin_bswap32(*((Oid *)PQgetvalue(r, i, 0)));
|
||||
snprintf(t->name, sizeof(t->name), "%s", PQgetvalue(r, i, 1));
|
||||
char typ = *PQgetvalue(r, i, 2);
|
||||
t->elemoid = __builtin_bswap32(*((Oid *)PQgetvalue(r, i, 3)));
|
||||
|
||||
/* enum, can use text send/recv */
|
||||
if (typ == 'e') {
|
||||
if (t->elemoid) {
|
||||
/* array */
|
||||
t->send = fupg_send_array;
|
||||
t->recv = fupg_recv_array;
|
||||
} else if (typ == 'e') {
|
||||
/* enum, can use text send/recv */
|
||||
t->send = fupg_send_text;
|
||||
t->recv = fupg_recv_text;
|
||||
continue;
|
||||
}
|
||||
/* TODO: Array types, records, custom overrides, 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;
|
||||
} else {
|
||||
/* TODO: records, (multi)ranges, custom overrides, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
PQclear(r);
|
||||
|
|
@ -437,6 +446,27 @@ static void fupg_st_check_dupcols(pTHX_ PGresult *r) {
|
|||
SvREFCNT_dec((SV *)hv);
|
||||
}
|
||||
|
||||
static void fupg_params_send(pTHX_ fupg_st *st, Oid oid, SV *val, fustr *out, int *refresh_done) {
|
||||
fupg_send send, elem;
|
||||
const fupg_type *t = fupg_lookup_type(aTHX_ st->conn, refresh_done, oid);
|
||||
if (!t) fu_confess("No type found with oid %u", oid);
|
||||
if (!t->send) fu_confess("Unable to use type '%s' (oid %u) as bind parameter", t->name, t->oid);
|
||||
send.oid = oid;
|
||||
send.name = t->name;
|
||||
send.fn = t->send;
|
||||
if (t->send == fupg_send_array) {
|
||||
if (!t->elemoid) fu_confess("Type '%s' (oid %u) is marked as an array type, but element type is unknown", t->name, t->oid);
|
||||
const fupg_type *e = fupg_lookup_type(aTHX_ st->conn, refresh_done, t->elemoid);
|
||||
if (!e) fu_confess("No type found with oid %u", t->elemoid);
|
||||
send.arrayelem = &elem;
|
||||
elem.oid = e->oid;
|
||||
elem.name = e->name;
|
||||
elem.fn = e->send;
|
||||
assert(e->send != fupg_send_array); /* TODO: might as well fix this, we'll need recursion for record types anyway */
|
||||
}
|
||||
send.fn(aTHX_ &send, val, out);
|
||||
}
|
||||
|
||||
static void fupg_params_setup(pTHX_ fupg_st *st, int *refresh_done) {
|
||||
int i;
|
||||
st->param_values = safecalloc(st->nbind, sizeof(*st->param_values));
|
||||
|
|
@ -456,15 +486,8 @@ static void fupg_params_setup(pTHX_ fupg_st *st, int *refresh_done) {
|
|||
st->param_values[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
fupg_send send;
|
||||
send.oid = PQparamtype(st->describe, i);
|
||||
const fupg_type *t = fupg_lookup_type(aTHX_ st->conn, refresh_done, send.oid);
|
||||
if (!t) fu_confess("No type found with oid %u", send.oid);
|
||||
if (!t->send) fu_confess("Unable to use type '%s' (oid %u) as bind parameter", t->name, t->oid);
|
||||
send.name = t->name;
|
||||
send.fn = t->send;
|
||||
off = fustr_len(buf);
|
||||
send.fn(aTHX_ &send, st->bind[i], buf);
|
||||
fupg_params_send(aTHX_ st, PQparamtype(st->describe, i), st->bind[i], buf, refresh_done);
|
||||
st->param_lengths[i] = fustr_len(buf) - off;
|
||||
st->param_formats[i] = 1;
|
||||
st->param_values[i] = "";
|
||||
|
|
@ -480,23 +503,32 @@ static void fupg_params_setup(pTHX_ fupg_st *st, int *refresh_done) {
|
|||
}
|
||||
}
|
||||
|
||||
static void fupg_results_setup(pTHX_ fupg_st *st, int *refresh_done) {
|
||||
int i;
|
||||
st->recv = safecalloc(st->nfields, sizeof(*st->recv));
|
||||
static void fupg_recv_setup(pTHX_ fupg_st *st, fupg_recv *r, Oid oid, int *refresh_done) {
|
||||
r->oid = oid;
|
||||
if (st->stflags & FUPG_TEXT_RESULTS) {
|
||||
for (i=0; i<st->nfields; i++)
|
||||
st->recv[i].fn = fupg_recv_text;
|
||||
r->name = "{textfmt}";
|
||||
r->fn = fupg_recv_text;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0; i<st->nfields; i++) {
|
||||
fupg_recv *r = st->recv + i;
|
||||
r->oid = PQftype(st->result, i);
|
||||
const fupg_type *t = fupg_lookup_type(aTHX_ st->conn, refresh_done, r->oid);
|
||||
if (!t) fu_confess("No type found with oid %u", r->oid);
|
||||
if (!t->recv) fu_confess("Unable to receive data of type '%s' (oid %u)", t->name, t->oid);
|
||||
r->name = t->name;
|
||||
r->fn = t->recv;
|
||||
const fupg_type *t = fupg_lookup_type(aTHX_ st->conn, refresh_done, r->oid);
|
||||
if (!t) fu_confess("No type found with oid %u", r->oid);
|
||||
if (!t->recv) fu_confess("Unable to receive data of type '%s' (oid %u)", t->name, t->oid);
|
||||
r->name = t->name;
|
||||
r->fn = t->recv;
|
||||
|
||||
if (r->fn == fupg_recv_array) {
|
||||
if (!t->elemoid) fu_confess("Type '%s' (oid %u) is marked as an array type, but element type is unknown", t->name, t->oid);
|
||||
r->arrayelem = safecalloc(1, sizeof(*r->arrayelem));
|
||||
fupg_recv_setup(aTHX_ st, r->arrayelem, t->elemoid, refresh_done);
|
||||
}
|
||||
}
|
||||
|
||||
static void fupg_recv_free(fupg_recv *r) {
|
||||
if (!r) return;
|
||||
if (r->fn == fupg_recv_array) {
|
||||
fupg_recv_free(r->arrayelem);
|
||||
safefree(r->arrayelem);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -539,7 +571,10 @@ static void fupg_st_execute(pTHX_ fupg_st *st) {
|
|||
st->result = r;
|
||||
|
||||
st->nfields = PQnfields(r);
|
||||
fupg_results_setup(aTHX_ st, &refresh_done);
|
||||
st->recv = safecalloc(st->nfields, sizeof(*st->recv));
|
||||
int i;
|
||||
for (i=0; i<st->nfields; i++)
|
||||
fupg_recv_setup(aTHX_ st, st->recv + i, PQftype(st->result, i), &refresh_done);
|
||||
}
|
||||
|
||||
static SV *fupg_st_getval(pTHX_ fupg_st *st, int row, int col) {
|
||||
|
|
@ -618,6 +653,7 @@ static void fupg_st_destroy(fupg_st *st) {
|
|||
safefree(st->param_values);
|
||||
safefree(st->param_lengths);
|
||||
safefree(st->param_formats);
|
||||
if (st->recv) for (i=0; i<st->nfields; i++) fupg_recv_free(st->recv + i);
|
||||
safefree(st->recv);
|
||||
PQclear(st->describe);
|
||||
PQclear(st->result);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue