pg: Pipeline prepare + describeprepared

This commit is contained in:
Yorhel 2025-02-09 09:26:01 +01:00
parent 7b76d94719
commit b6517cf05a
2 changed files with 43 additions and 14 deletions

View file

@ -40,11 +40,12 @@ typedef enum { PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR, P
X(PQclosePrepared, PGresult *, PGconn *, const char *) \
X(PQcmdTuples, char *, PGresult *) \
X(PQconnectdb, PGconn *, const char *) \
X(PQdescribePrepared, PGresult *, PGconn *, const char *) \
X(PQenterPipelineMode, int, PGconn *) \
X(PQerrorMessage, char *, const PGconn *) \
X(PQexec, PGresult *, PGconn *, const char *) \
X(PQexecParams, PGresult *, PGconn *, const char *, int, const Oid *, const char * const *, const int *, const int *, int) \
X(PQexecPrepared, PGresult *, PGconn *, const char *, int, const char * const *, const int *, const int *, int) \
X(PQexitPipelineMode, int, PGconn *conn) \
X(PQfinish, void, PGconn *) \
X(PQfmod, int, const PGresult *, int) \
X(PQfname, char *, const PGresult *, int) \
@ -52,18 +53,21 @@ typedef enum { PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR, P
X(PQftype, Oid, const PGresult *, int) \
X(PQgetisnull, int, const PGresult *, int, int) \
X(PQgetlength, int, const PGresult *, int, int) \
X(PQgetResult, PGresult *, PGconn *) \
X(PQgetvalue, char *, const PGresult *, int, int) \
X(PQlibVersion, int, void) \
X(PQnfields, int, const PGresult *) \
X(PQnparams, int, const PGresult *) \
X(PQntuples, int, const PGresult *) \
X(PQparamtype, Oid, const PGresult *, int) \
X(PQprepare, PGresult *, PGconn *, const char *, const char *, int, const Oid *) \
X(PQpipelineSync, int, PGconn *) \
X(PQresStatus, char *, ExecStatusType) \
X(PQresultErrorField, char *, const PGresult *, int) \
X(PQresultErrorMessage, char *, const PGresult *res) \
X(PQresultErrorMessage, char *, const PGresult *) \
X(PQresultStatus, ExecStatusType, const PGresult *) \
X(PQresultVerboseErrorMessage, char *, const PGresult *, PGVerbosity, PGContextVisibility) \
X(PQsendDescribePrepared, int, PGconn *, const char *) \
X(PQsendPrepare, int, PGconn *, const char *, const char *, int, const Oid *) \
X(PQserverVersion, int, const PGconn *) \
X(PQstatus, ConnStatusType, const PGconn *) \
X(PQtrace, void, PGconn *, FILE *) \

View file

@ -261,6 +261,9 @@ static void fupg_txn_destroy(pTHX_ fupg_txn *t) {
/* XXX: It feels a bit wasteful to load *all* types; even on an empty database
* that's ~55k of data, but it's easier and (potentially) faster than fetching
* each type seperately as we encounter them.
* Perhaps an easier optimization is to filter out all table-based composites
* and their array types by default, I've never seen anyone use those types for
* I/O and that would shrink the data by nearly a factor 5.
*/
static void fupg_refresh_types(pTHX_ fupg_conn *c) {
safefree(c->types);
@ -354,19 +357,41 @@ static void fupg_st_prepare(pTHX_ fupg_st *st) {
snprintf(st->name, sizeof(st->name), "fupg%"UVuf, ++st->conn->prep_counter);
/* TODO: Pipeline these two commands, no need for two round-trips with the server */
PGresult *r = PQprepare(st->conn->conn, st->name, st->query, 0, NULL);
if (!r) fupg_conn_croak(st->conn , "prepare");
if (PQresultStatus(r) != PGRES_COMMAND_OK)
fupg_result_croak(r, "prepare", st->query);
PQclear(r);
/* Send prepare + describe in a pipeline to avoid a double round-trip with the server */
PQenterPipelineMode(st->conn->conn);
PQsendPrepare(st->conn->conn, st->name, st->query, 0, NULL);
PQsendDescribePrepared(st->conn->conn, st->name);
PQpipelineSync(st->conn->conn);
PGresult *prep = PQgetResult(st->conn->conn); PQgetResult(st->conn->conn); /* NULL */
PGresult *desc = PQgetResult(st->conn->conn); PQgetResult(st->conn->conn); /* NULL */
PGresult *sync = PQgetResult(st->conn->conn);
PQexitPipelineMode(st->conn->conn);
if (!prep) {
PQclear(desc); PQclear(sync);
fupg_conn_croak(st->conn , "prepare");
}
if (PQresultStatus(prep) != PGRES_COMMAND_OK) {
PQclear(desc); PQclear(sync);
fupg_result_croak(prep, "prepare", st->query);
}
PQclear(prep);
st->prepared = 1;
r = PQdescribePrepared(st->conn->conn, st->name);
if (!r) fupg_conn_croak(st->conn , "prepare");
if (PQresultStatus(r) != PGRES_COMMAND_OK)
fupg_result_croak(r, "prepare", st->query);
st->describe = r;
if (!desc) {
PQclear(sync);
fupg_conn_croak(st->conn , "prepare");
}
if (PQresultStatus(desc) != PGRES_COMMAND_OK) {
PQclear(sync);
fupg_result_croak(desc, "prepare", st->query);
}
st->describe = desc;
if (!sync) fupg_conn_croak(st->conn , "prepare");
if (PQresultStatus(sync) != PGRES_PIPELINE_SYNC)
fupg_result_croak(sync, "prepare", st->query);
PQclear(sync);
}
static SV *fupg_st_params(pTHX_ fupg_st *st) {