diff --git a/c/libpq.h b/c/libpq.h index e3cf312..f931016 100644 --- a/c/libpq.h +++ b/c/libpq.h @@ -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 *) \ diff --git a/c/pgconn.c b/c/pgconn.c index c12c465..606207c 100644 --- a/c/pgconn.c +++ b/c/pgconn.c @@ -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) {