FU
view release on metacpan or search on metacpan
}
int i, nfields = PQnfields(r);
AV *av = nfields == 0 ? newAV() : newAV_alloc_x(nfields);
for (i=0; i<nfields; i++) {
HV *hv = newHV();
const char *name = PQfname(r, i);
hv_stores(hv, "name", newSVpvn_utf8(name, strlen(name), 1));
hv_stores(hv, "oid", newSViv(PQftype(r, i)));
int tmod = PQfmod(r, i);
if (tmod >= 0) hv_stores(hv, "typemod", newSViv(tmod));
av_push_simple(av, newRV_noinc((SV *)hv));
}
return sv_2mortal(newRV_noinc((SV *)av));
}
static void fupg_params_setup(pTHX_ fupg_st *st, int *refresh_done) {
int i;
st->param_values = safecalloc(st->nbind, sizeof(*st->param_values));
if (st->stflags & FUPG_TEXT_PARAMS) {
for (i=0; i<st->nbind; i++)
st->param_values[i] = !SvOK(st->bind[i]) ? NULL : SvPVutf8_nolen(st->bind[i]);
return;
}
fustr *buf = &st->conn->buf;
buf->cur = fustr_start(buf);
st->param_lengths = safecalloc(st->nbind, sizeof(*st->param_lengths));
st->param_formats = safecalloc(st->nbind, sizeof(*st->param_formats));
size_t off = 0;
for (i=0; i<st->nbind; i++) {
if (!SvOK(st->bind[i])) {
st->param_values[i] = NULL;
continue;
}
fupg_tio_setup(aTHX_ st->conn, &st->send,
FUPGT_SEND | (st->stflags & FUPG_TEXT_PARAMS ? FUPGT_TEXT : 0),
PQparamtype(st->describe, i), refresh_done);
off = fustr_len(buf);
st->send.send(aTHX_ &st->send, st->bind[i], buf);
fupg_tio_free(&st->send);
memset(&st->send, 0, sizeof(st->send));
st->param_lengths[i] = fustr_len(buf) - off;
st->param_formats[i] = 1;
st->param_values[i] = "";
/* Don't write param_values here, the buffer may be invalidated when writing the next param */
}
off = 0;
buf->cur = fustr_start(buf);
for (i=0; i<st->nbind; i++) {
if (st->param_values[i]) {
st->param_values[i] = buf->cur + off;
off += st->param_lengths[i];
}
}
}
static void fupg_st_execute(pTHX_ fupg_st *st) {
/* Disallow fetching the results more than once. I don't see a reason why
* someone would need that and disallowing it leaves room for fetching the
* results in a streaming fashion without breaking API compat. */
if (st->result) fu_confess("Invalid attempt to execute statement multiple times");
/* Whether we can do a direct call or need to prepare first */
int direct = !st->describe && (st->nbind == 0 || st->stflags & FUPG_TEXT_PARAMS) && !(st->stflags & FUPG_CACHE);
if (!direct) {
fupg_st_prepare(aTHX_ st);
if (PQnparams(st->describe) != st->nbind)
fu_confess("Statement expects %d bind parameters but %d were given", PQnparams(st->describe), st->nbind);
}
int refresh_done = 0;
fupg_params_setup(aTHX_ st, &refresh_done);
/* I'm not super fond of this approach. Storing the full query results in a
* PGresult involves unnecessary parsing, memory allocation and copying.
* The wire protocol is sufficiently simple that I could parse the query
* results directly from the network buffers without much additional code,
* and that would be much more efficient. Alas, libpq doesn't let me do
* that.
* There is the option of fetching results in chunked mode, but from what I
* gather that just saves a bit of memory in exchange for more and smaller
* malloc()/free()'s. Performance-wise, it probably won't be much of an
* improvement */
struct timespec t_start;
clock_gettime(CLOCK_MONOTONIC, &t_start);
PGresult *r = direct ? PQexecParams(st->conn->conn,
st->query, st->nbind, NULL,
(const char * const *)st->param_values,
st->param_lengths, st->param_formats,
st->stflags & FUPG_TEXT_RESULTS ? 0 : 1
) : PQexecPrepared(st->conn->conn,
st->name, st->nbind,
(const char * const *)st->param_values,
st->param_lengths, st->param_formats,
st->stflags & FUPG_TEXT_RESULTS ? 0 : 1
);
struct timespec t_end;
clock_gettime(CLOCK_MONOTONIC, &t_end);
st->exectime = fu_timediff(&t_end, &t_start);
if (!r) fupg_conn_croak(st->conn , "exec");
switch (PQresultStatus(r)) {
case PGRES_COMMAND_OK:
case PGRES_TUPLES_OK: break;
default: fupg_result_croak(r, "exec", st->query);
}
st->result = r;
st->nfields = PQnfields(r);
st->recv = safecalloc(st->nfields, sizeof(*st->recv));
int i;
for (i=0; i<st->nfields; i++)
fupg_tio_setup(aTHX_ st->conn, st->recv + i,
FUPGT_RECV | (st->stflags & FUPG_TEXT_RESULTS ? FUPGT_TEXT : 0),
PQftype(st->result, i), &refresh_done);
fupg_tracecb(aTHX_ st);
}
static SV *fupg_st_getval(pTHX_ fupg_st *st, int row, int col) {
PGresult *r = st->result;
( run in 1.462 second using v1.01-cache-2.11-cpan-5837b0d9d2c )