From 1f35373081aa9b2c5143e7af366211302eb997fc Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Fri, 22 May 2026 10:40:38 -0700 Subject: [PATCH v2 2/3] stop using PQfn() in libpq's LO interface --- src/interfaces/libpq/fe-connect.c | 7 +- src/interfaces/libpq/fe-lobj.c | 672 ++++++++++++++---------------- src/interfaces/libpq/libpq-int.h | 22 +- src/tools/pgindent/typedefs.list | 1 + 4 files changed, 319 insertions(+), 383 deletions(-) diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 5e41c21c6f6..9d0e0fd6798 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -692,10 +692,9 @@ pqDropServerData(PGconn *conn) conn->in_hot_standby = PG_BOOL_UNKNOWN; conn->scram_sha_256_iterations = SCRAM_SHA_256_DEFAULT_ITERATIONS; conn->sversion = 0; - - /* Drop large-object lookup data */ - free(conn->lobjfuncs); - conn->lobjfuncs = NULL; + conn->lobjprepared = false; + conn->lobjprepmap = 0; + conn->lo_dealloc_cb_set = false; /* Reset assorted other per-connection state */ conn->last_sqlstate[0] = '\0'; diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c index 12a32fcbaf3..0d3d7c1c3ed 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -41,10 +41,116 @@ #define LO_BUFSIZE 8192 +typedef enum PGlobjfunctype +{ + LO_OPEN, + LO_CLOSE, + LO_CREAT, + LO_CREATE, + LO_UNLINK, + LO_LSEEK, + LO_LSEEK64, + LO_TELL, + LO_TELL64, + LO_TRUNCATE, + LO_TRUNCATE64, + LO_READ, + LO_WRITE, +} PGlobjfunctype; + +typedef struct PGlobjfuncs +{ + char *name; + char *query; + int version; +} PGlobjfuncs; + +static const PGlobjfuncs lobjfuncs[] = +{ + [LO_OPEN] = { + "libpq_internal_lo_open", + "SELECT pg_catalog.lo_open($1::pg_catalog.oid, $2::pg_catalog.int4)" + }, + [LO_CLOSE] = { + "libpq_internal_lo_close", + "SELECT pg_catalog.lo_close($1::pg_catalog.int4)" + }, + [LO_CREAT] = { + "libpq_internal_lo_creat", + "SELECT pg_catalog.lo_creat($1::pg_catalog.int4)" + }, + [LO_CREATE] = { + "libpq_internal_lo_create", + "SELECT pg_catalog.lo_create($1::pg_catalog.oid)", + 80100 + }, + [LO_UNLINK] = { + "libpq_internal_lo_unlink", + "SELECT pg_catalog.lo_unlink($1::pg_catalog.oid)" + }, + [LO_LSEEK] = { + "libpq_internal_lo_lseek", + "SELECT pg_catalog.lo_lseek($1::pg_catalog.int4, $2::pg_catalog.int4, $3::pg_catalog.int4)" + }, + [LO_LSEEK64] = { + "libpq_internal_lo_lseek64", + "SELECT pg_catalog.lo_lseek64($1::pg_catalog.int4, $2::pg_catalog.int8, $3::pg_catalog.int4)", + 90300 + }, + [LO_TELL] = { + "libpq_internal_lo_tell", + "SELECT pg_catalog.lo_tell($1::pg_catalog.int4)" + }, + [LO_TELL64] = { + "libpq_internal_lo_tell64", + "SELECT pg_catalog.lo_tell64($1::pg_catalog.int4)", + 90300 + }, + [LO_TRUNCATE] = { + "libpq_internal_lo_truncate", + "SELECT pg_catalog.lo_truncate($1::pg_catalog.int4, $2::pg_catalog.int4)", + 80300 + }, + [LO_TRUNCATE64] = { + "libpq_internal_lo_truncate64", + "SELECT pg_catalog.lo_truncate64($1::pg_catalog.int4, $2::pg_catalog.int8)", + 90300 + }, + [LO_READ] = { + "libpq_internal_loread", + "SELECT pg_catalog.loread($1::pg_catalog.int4, $2::pg_catalog.int4)" + }, + [LO_WRITE] = { + "libpq_internal_lowrite", + "SELECT pg_catalog.lowrite($1::pg_catalog.int4, $2::pg_catalog.bytea)" + } +}; + static int lo_initialize(PGconn *conn); static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid); -static int64_t lo_hton64(int64_t host64); -static int64_t lo_ntoh64(int64_t net64); + +static bool +lo_result_is_valid(const PGresult *res, int len) +{ + return PQresultStatus(res) == PGRES_TUPLES_OK && + PQntuples(res) == 1 && + PQnfields(res) == 1 && + PQgetisnull(res, 0, 0) == 0 && + PQfformat(res, 0) == 1 && + (len == -1 || PQgetlength(res, 0, 0) == len); +} + +static PGresult * +lo_exec(PGconn *conn, PGlobjfunctype type, int nargs, char **argv, + const int *argLens, const int *argFmts) +{ + if (conn->lobjprepared) + return PQexecPrepared(conn, lobjfuncs[type].name, nargs, + (const char *const *) argv, argLens, argFmts, 1); + else + return PQexecParams(conn, lobjfuncs[type].query, nargs, NULL, + (const char *const *) argv, argLens, argFmts, 1); +} /* * lo_open @@ -57,26 +163,26 @@ int lo_open(PGconn *conn, Oid lobjId, int mode) { int fd; - int result_len; - PQArgBlock argv[2]; + char *argv[2]; + int argLens[] = {4, 4}; + int argFmts[] = {1, 1}; PGresult *res; if (lo_initialize(conn) < 0) return -1; - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = lobjId; + lobjId = pg_hton32(lobjId); + argv[0] = (char *) &lobjId; - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = mode; + mode = pg_hton32(mode); + argv[1] = (char *) &mode; - res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_OPEN, 2, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(fd))) { + memcpy(&fd, PQgetvalue(res, 0, 0), sizeof(fd)); PQclear(res); - return fd; + return pg_ntoh32(fd); } else { @@ -95,23 +201,24 @@ lo_open(PGconn *conn, Oid lobjId, int mode) int lo_close(PGconn *conn, int fd) { - PQArgBlock argv[1]; + char *argv[1]; + int argLens[] = {4}; + int argFmts[] = {1}; PGresult *res; int retval; - int result_len; if (lo_initialize(conn) < 0) return -1; - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - res = PQfn(conn, conn->lobjfuncs->fn_lo_close, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + fd = pg_hton32(fd); + argv[0] = (char *) &fd; + + res = lo_exec(conn, LO_CLOSE, 1, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return retval; + return pg_ntoh32(retval); } else { @@ -130,18 +237,19 @@ lo_close(PGconn *conn, int fd) int lo_truncate(PGconn *conn, int fd, size_t len) { - PQArgBlock argv[2]; + char *argv[2]; + int len32 = len; + int argLens[] = {4, 4}; + int argFmts[] = {1, 1}; PGresult *res; int retval; - int result_len; if (lo_initialize(conn) < 0) return -1; - /* Must check this on-the-fly because it's not there pre-8.3 */ - if (conn->lobjfuncs->fn_lo_truncate == 0) + if (PQserverVersion(conn) < 80300) { - libpq_append_conn_error(conn, "cannot determine OID of function %s", + libpq_append_conn_error(conn, "server does not support function \"%s\"", "lo_truncate"); return -1; } @@ -161,21 +269,18 @@ lo_truncate(PGconn *conn, int fd, size_t len) return -1; } - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = (int) len; + len32 = pg_hton32(len32); + argv[1] = (char *) &len32; - res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate, - &retval, &result_len, 1, argv, 2); - - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_TRUNCATE, 2, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return retval; + return pg_ntoh32(retval); } else { @@ -194,37 +299,34 @@ lo_truncate(PGconn *conn, int fd, size_t len) int lo_truncate64(PGconn *conn, int fd, int64_t len) { - PQArgBlock argv[2]; + char *argv[2]; + int argLens[] = {4, 8}; + int argFmts[] = {1, 1}; PGresult *res; int retval; - int result_len; if (lo_initialize(conn) < 0) return -1; - if (conn->lobjfuncs->fn_lo_truncate64 == 0) + if (PQserverVersion(conn) < 90300) { - libpq_append_conn_error(conn, "cannot determine OID of function %s", + libpq_append_conn_error(conn, "server does not support function \"%s\"", "lo_truncate64"); return -1; } - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - len = lo_hton64(len); - argv[1].isint = 0; - argv[1].len = 8; - argv[1].u.ptr = (int *) &len; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64, - &retval, &result_len, 1, argv, 2); + len = pg_hton64(len); + argv[1] = (char *) &len; - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_TRUNCATE64, 2, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return retval; + return pg_ntoh32(retval); } else { @@ -244,9 +346,11 @@ lo_truncate64(PGconn *conn, int fd, int64_t len) int lo_read(PGconn *conn, int fd, char *buf, size_t len) { - PQArgBlock argv[2]; + char *argv[2]; + int len32 = len; + int argLens[] = {4, 4}; + int argFmts[] = {1, 1}; PGresult *res; - int result_len; if (lo_initialize(conn) < 0) return -1; @@ -263,18 +367,22 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len) return -1; } - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = (int) len; + len32 = pg_hton32(len32); + argv[1] = (char *) &len32; - res = PQnfn(conn, conn->lobjfuncs->fn_lo_read, - (void *) buf, len, &result_len, 0, argv, 2); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_READ, 2, argv, argLens, argFmts); + if (lo_result_is_valid(res, -1)) { + int result_len = PQgetlength(res, 0, 0); + + if (result_len > len) + result_len = -1; + else + memcpy(buf, PQgetvalue(res, 0, 0), result_len); + PQclear(res); return result_len; } @@ -294,9 +402,10 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len) int lo_write(PGconn *conn, int fd, const char *buf, size_t len) { - PQArgBlock argv[2]; + char *argv[2]; + int argLens[] = {4, len}; + int argFmts[] = {1, 1}; PGresult *res; - int result_len; int retval; if (lo_initialize(conn) < 0) @@ -314,20 +423,17 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len) return -1; } - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - argv[1].isint = 0; - argv[1].len = (int) len; - argv[1].u.ptr = (int *) unconstify(char *, buf); + argv[1] = unconstify(char *, buf); - res = PQfn(conn, conn->lobjfuncs->fn_lo_write, - &retval, &result_len, 1, argv, 2); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_WRITE, 2, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return retval; + return pg_ntoh32(retval); } else { @@ -343,32 +449,30 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len) int lo_lseek(PGconn *conn, int fd, int offset, int whence) { - PQArgBlock argv[3]; + char *argv[3]; + int argLens[] = {4, 4, 4}; + int argFmts[] = {1, 1, 1}; PGresult *res; int retval; - int result_len; if (lo_initialize(conn) < 0) return -1; - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = offset; + offset = pg_hton32(offset); + argv[1] = (char *) &offset; - argv[2].isint = 1; - argv[2].len = 4; - argv[2].u.integer = whence; + whence = pg_hton32(whence); + argv[2] = (char *) &whence; - res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek, - &retval, &result_len, 1, argv, 3); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_LSEEK, 3, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return retval; + return pg_ntoh32(retval); } else { @@ -384,40 +488,37 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence) int64_t lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence) { - PQArgBlock argv[3]; + char *argv[3]; + int argLens[] = {4, 8, 4}; + int argFmts[3] = {1, 1, 1}; PGresult *res; int64 retval; - int result_len; if (lo_initialize(conn) < 0) return -1; - if (conn->lobjfuncs->fn_lo_lseek64 == 0) + if (PQserverVersion(conn) < 90300) { - libpq_append_conn_error(conn, "cannot determine OID of function %s", + libpq_append_conn_error(conn, "server does not support function \"%s\"", "lo_lseek64"); return -1; } - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - offset = lo_hton64(offset); - argv[1].isint = 0; - argv[1].len = 8; - argv[1].u.ptr = (int *) &offset; + offset = pg_hton64(offset); + argv[1] = (char *) &offset; - argv[2].isint = 1; - argv[2].len = 4; - argv[2].u.integer = whence; + whence = pg_hton32(whence); + argv[2] = (char *) &whence; - res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek64, - (void *) &retval, sizeof(retval), &result_len, 0, argv, 3); - if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) + res = lo_exec(conn, LO_LSEEK64, 3, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return lo_ntoh64(retval); + return pg_ntoh64(retval); } else { @@ -437,23 +538,24 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence) Oid lo_creat(PGconn *conn, int mode) { - PQArgBlock argv[1]; + char *argv[1]; + int argLens[] = {4}; + int argFmts[] = {1}; PGresult *res; int retval; - int result_len; if (lo_initialize(conn) < 0) return InvalidOid; - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = mode; - res = PQfn(conn, conn->lobjfuncs->fn_lo_creat, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + mode = pg_hton32(mode); + argv[0] = (char *) &mode; + + res = lo_exec(conn, LO_CREAT, 1, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return (Oid) retval; + return (Oid) pg_ntoh32(retval); } else { @@ -473,31 +575,31 @@ lo_creat(PGconn *conn, int mode) Oid lo_create(PGconn *conn, Oid lobjId) { - PQArgBlock argv[1]; + char *argv[1]; + int argLens[] = {4}; + int argFmts[] = {1}; PGresult *res; int retval; - int result_len; if (lo_initialize(conn) < 0) return InvalidOid; - /* Must check this on-the-fly because it's not there pre-8.1 */ - if (conn->lobjfuncs->fn_lo_create == 0) + if (PQserverVersion(conn) < 80100) { - libpq_append_conn_error(conn, "cannot determine OID of function %s", + libpq_append_conn_error(conn, "server does not support function \"%s\"", "lo_create"); return InvalidOid; } - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = lobjId; - res = PQfn(conn, conn->lobjfuncs->fn_lo_create, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + lobjId = pg_hton32(lobjId); + argv[0] = (char *) &lobjId; + + res = lo_exec(conn, LO_CREATE, 1, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return (Oid) retval; + return (Oid) pg_ntoh32(retval); } else { @@ -515,23 +617,23 @@ int lo_tell(PGconn *conn, int fd) { int retval; - PQArgBlock argv[1]; + char *argv[1]; + int argLens[] = {4}; + int argFmts[] = {1}; PGresult *res; - int result_len; if (lo_initialize(conn) < 0) return -1; - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - res = PQfn(conn, conn->lobjfuncs->fn_lo_tell, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_TELL, 1, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return retval; + return pg_ntoh32(retval); } else { @@ -548,30 +650,30 @@ int64_t lo_tell64(PGconn *conn, int fd) { int64 retval; - PQArgBlock argv[1]; + char *argv[1]; + int argLens[] = {4}; + int argFmts[] = {1}; PGresult *res; - int result_len; if (lo_initialize(conn) < 0) return -1; - if (conn->lobjfuncs->fn_lo_tell64 == 0) + if (PQserverVersion(conn) < 90300) { - libpq_append_conn_error(conn, "cannot determine OID of function %s", + libpq_append_conn_error(conn, "server does not support functions \"%s\"", "lo_tell64"); return -1; } - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; + fd = pg_hton32(fd); + argv[0] = (char *) &fd; - res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell64, - (void *) &retval, sizeof(retval), &result_len, 0, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) + res = lo_exec(conn, LO_TELL64, 1, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return lo_ntoh64(retval); + return pg_ntoh64(retval); } else { @@ -588,24 +690,24 @@ lo_tell64(PGconn *conn, int fd) int lo_unlink(PGconn *conn, Oid lobjId) { - PQArgBlock argv[1]; + char *argv[1]; + int argLens[] = {4}; + int argFmts[] = {1}; PGresult *res; - int result_len; int retval; if (lo_initialize(conn) < 0) return -1; - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = lobjId; + lobjId = pg_hton32(lobjId); + argv[0] = (char *) &lobjId; - res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) + res = lo_exec(conn, LO_UNLINK, 1, argv, argLens, argFmts); + if (lo_result_is_valid(res, sizeof(retval))) { + memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval)); PQclear(res); - return retval; + return pg_ntoh32(retval); } else { @@ -829,6 +931,30 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) return result; } +/* + * LO prepared statement deallocation callback. Resets conn->lobjprepared and + * the corresponding bits in conn->lobjprepmap so the next call to + * lo_initialize() re-prepares as needed. + */ +static void +lo_dealloc_callback(PGconn *conn, const char *name) +{ + if (name[0] == '\0') + { + conn->lobjprepared = false; + conn->lobjprepmap = 0; + return; + } + + for (int i = 0; i < lengthof(lobjfuncs); i++) + { + if (strcmp(name, lobjfuncs[i].name) != 0) + continue; + + conn->lobjprepmap &= ~(1 << i); + break; + } +} /* * lo_initialize @@ -836,19 +962,12 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) * Initialize for a new large-object operation on an existing connection. * Return 0 if OK, -1 on failure. * - * If we haven't previously done so, we collect the function OIDs from - * pg_proc for all functions that are required for large object operations. + * If we haven't previously done so, we prepared statements for all the + * functions that are required for large object operations. */ static int lo_initialize(PGconn *conn) { - PGresult *res; - PGlobjfuncs *lobjfuncs; - int n; - const char *query; - const char *fname; - Oid foid; - /* Nothing we can do with no connection */ if (conn == NULL) return -1; @@ -857,208 +976,41 @@ lo_initialize(PGconn *conn) pqClearConnErrorState(conn); /* Nothing else to do if we already collected info */ - if (conn->lobjfuncs != NULL) + if (conn->lobjprepared) return 0; - /* - * Allocate the structure to hold the function OIDs. We don't store it - * into the PGconn until it's successfully filled. - */ - lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs)); - if (lobjfuncs == NULL) - { - libpq_append_conn_error(conn, "out of memory"); - return -1; - } - MemSet(lobjfuncs, 0, sizeof(PGlobjfuncs)); - - /* - * Execute the query to get all the functions at once. (Not all of them - * may exist in older server versions.) - */ - query = "select proname, oid from pg_catalog.pg_proc " - "where proname in (" - "'lo_open', " - "'lo_close', " - "'lo_creat', " - "'lo_create', " - "'lo_unlink', " - "'lo_lseek', " - "'lo_lseek64', " - "'lo_tell', " - "'lo_tell64', " - "'lo_truncate', " - "'lo_truncate64', " - "'loread', " - "'lowrite') " - "and pronamespace = (select oid from pg_catalog.pg_namespace " - "where nspname = 'pg_catalog')"; + /* Use PQexecParams() on servers that don't have dealloc notifications */ + if (PQfullProtocolVersion(conn) < 30003) + return 0; - res = PQexec(conn, query); - if (res == NULL) + if (!conn->lo_dealloc_cb_set) { - free(lobjfuncs); - return -1; + PQaddPrepStmtDeallocCallback(conn, lo_dealloc_callback); + conn->lo_dealloc_cb_set = true; } - if (res->resultStatus != PGRES_TUPLES_OK) + for (int i = 0; i < lengthof(lobjfuncs); i++) { - free(lobjfuncs); - PQclear(res); - libpq_append_conn_error(conn, "query to initialize large object functions did not return data"); - return -1; - } + PGresult *res; - /* - * Examine the result and put the OID's into the struct - */ - for (n = 0; n < PQntuples(res); n++) - { - fname = PQgetvalue(res, n, 0); - foid = (Oid) atoi(PQgetvalue(res, n, 1)); - if (strcmp(fname, "lo_open") == 0) - lobjfuncs->fn_lo_open = foid; - else if (strcmp(fname, "lo_close") == 0) - lobjfuncs->fn_lo_close = foid; - else if (strcmp(fname, "lo_creat") == 0) - lobjfuncs->fn_lo_creat = foid; - else if (strcmp(fname, "lo_create") == 0) - lobjfuncs->fn_lo_create = foid; - else if (strcmp(fname, "lo_unlink") == 0) - lobjfuncs->fn_lo_unlink = foid; - else if (strcmp(fname, "lo_lseek") == 0) - lobjfuncs->fn_lo_lseek = foid; - else if (strcmp(fname, "lo_lseek64") == 0) - lobjfuncs->fn_lo_lseek64 = foid; - else if (strcmp(fname, "lo_tell") == 0) - lobjfuncs->fn_lo_tell = foid; - else if (strcmp(fname, "lo_tell64") == 0) - lobjfuncs->fn_lo_tell64 = foid; - else if (strcmp(fname, "lo_truncate") == 0) - lobjfuncs->fn_lo_truncate = foid; - else if (strcmp(fname, "lo_truncate64") == 0) - lobjfuncs->fn_lo_truncate64 = foid; - else if (strcmp(fname, "loread") == 0) - lobjfuncs->fn_lo_read = foid; - else if (strcmp(fname, "lowrite") == 0) - lobjfuncs->fn_lo_write = foid; - } + if (conn->lobjprepmap & (1 << i)) + continue; - PQclear(res); + if (PQserverVersion(conn) < lobjfuncs[i].version) + continue; - /* - * Finally check that we got all required large object interface functions - * (ones that have been added later than the stone age are instead checked - * only if used) - */ - if (lobjfuncs->fn_lo_open == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "lo_open"); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_close == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "lo_close"); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_creat == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "lo_creat"); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_unlink == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "lo_unlink"); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_lseek == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "lo_lseek"); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_tell == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "lo_tell"); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_read == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "loread"); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_write == 0) - { - libpq_append_conn_error(conn, "cannot determine OID of function %s", - "lowrite"); - free(lobjfuncs); - return -1; + res = PQprepare(conn, lobjfuncs[i].name, lobjfuncs[i].query, 0, NULL); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + PQclear(res); + libpq_append_conn_error(conn, "query to prepare large object statements failed"); + return -1; + } + + PQclear(res); + conn->lobjprepmap |= (1 << i); } - /* - * Put the structure into the connection control - */ - conn->lobjfuncs = lobjfuncs; + conn->lobjprepared = true; return 0; } - -/* - * lo_hton64 - * converts a 64-bit integer from host byte order to network byte order - */ -static int64_t -lo_hton64(int64_t host64) -{ - union - { - int64 i64; - uint32 i32[2]; - } swap; - uint32 t; - - /* High order half first, since we're doing MSB-first */ - t = (uint32) (host64 >> 32); - swap.i32[0] = pg_hton32(t); - - /* Now the low order half */ - t = (uint32) host64; - swap.i32[1] = pg_hton32(t); - - return swap.i64; -} - -/* - * lo_ntoh64 - * converts a 64-bit integer from network byte order to host byte order - */ -static int64_t -lo_ntoh64(int64_t net64) -{ - union - { - int64 i64; - uint32 i32[2]; - } swap; - int64 result; - - swap.i64 = net64; - - result = (uint32) pg_ntoh32(swap.i32[0]); - result <<= 32; - result |= (uint32) pg_ntoh32(swap.i32[1]); - - return result; -} diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 7eca941ddcc..7a32c14de5e 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -275,24 +275,6 @@ typedef struct pgParameterStatus /* Note: name and value are stored in same malloc block as struct is */ } pgParameterStatus; -/* large-object-access data ... allocated only if large-object code is used. */ -typedef struct pgLobjfuncs -{ - Oid fn_lo_open; /* OID of backend function lo_open */ - Oid fn_lo_close; /* OID of backend function lo_close */ - Oid fn_lo_creat; /* OID of backend function lo_creat */ - Oid fn_lo_create; /* OID of backend function lo_create */ - Oid fn_lo_unlink; /* OID of backend function lo_unlink */ - Oid fn_lo_lseek; /* OID of backend function lo_lseek */ - Oid fn_lo_lseek64; /* OID of backend function lo_lseek64 */ - Oid fn_lo_tell; /* OID of backend function lo_tell */ - Oid fn_lo_tell64; /* OID of backend function lo_tell64 */ - Oid fn_lo_truncate; /* OID of backend function lo_truncate */ - Oid fn_lo_truncate64; /* OID of function lo_truncate64 */ - Oid fn_lo_read; /* OID of backend function LOread */ - Oid fn_lo_write; /* OID of backend function LOwrite */ -} PGlobjfuncs; - /* * PGdataValue represents a data field value being passed to a row processor. * It could be either text or binary data; text data is not zero-terminated. @@ -564,7 +546,9 @@ struct pg_conn PGTernaryBool in_hot_standby; /* in_hot_standby */ PGVerbosity verbosity; /* error/notice message verbosity */ PGContextVisibility show_context; /* whether to show CONTEXT field */ - PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */ + bool lobjprepared; /* whether LO statements have been prepared */ + uint32 lobjprepmap; /* bitmap of prepared LO statements */ + bool lo_dealloc_cb_set; /* whether prep stmt dealloc callback set */ pg_prng_state prng_state; /* prng state for load balancing connections */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 8cf40c87043..86b90010d2a 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1983,6 +1983,7 @@ PGcmdQueueEntry PGconn PGdataValue PGlobjfuncs +PGlobjfunctype PGnotify PGoauthBearerRequest PGoauthBearerRequestV2 -- 2.50.1 (Apple Git-155)