[PATCH v2 2/3] stop using PQfn() in libpq's LO interface
Nathan Bossart <nathan@postgresql.org>
From: Nathan Bossart <nathan@postgresql.org>
To:
Date: 2026-05-22T17:40:38Z
Lists: pgsql-hackers
---
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)
--wfzLmV/Wl0ehOJBp
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
filename=v2-0003-remove-support-for-PQfn.patch