[PATCH v1 1/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 | 5 +-
src/interfaces/libpq/fe-lobj.c | 656 ++++++++++++++----------------
src/interfaces/libpq/libpq-int.h | 20 +-
3 files changed, 299 insertions(+), 382 deletions(-)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 4272d386e64..01c9735f276 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -692,10 +692,7 @@ 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;
/* 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..41573c22c80 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -41,10 +41,101 @@
#define LO_BUFSIZE 8192
+typedef struct PGlobjfuncs
+{
+ char *name;
+ char *args;
+ int nargs;
+ int version;
+} PGlobjfuncs;
+
+static const PGlobjfuncs lobjfuncs[] =
+{
+ {
+ "lo_open",
+ "$1::pg_catalog.oid, $2::pg_catalog.int4",
+ 2
+ },
+ {
+ "lo_close",
+ "$1::pg_catalog.int4",
+ 1
+ },
+ {
+ "lo_creat",
+ "$1::pg_catalog.int4",
+ 1
+ },
+ {
+ "lo_create",
+ "$1::pg_catalog.oid",
+ 1,
+ 80100
+ },
+ {
+ "lo_unlink",
+ "$1::pg_catalog.oid",
+ 1
+ },
+ {
+ "lo_lseek",
+ "$1::pg_catalog.int4, $2::pg_catalog.int4, $3::pg_catalog.int4",
+ 3
+ },
+ {
+ "lo_lseek64",
+ "$1::pg_catalog.int4, $2::pg_catalog.int8, $3::pg_catalog.int4",
+ 3,
+ 90300
+ },
+ {
+ "lo_tell",
+ "$1::pg_catalog.int4",
+ 1
+ },
+ {
+ "lo_tell64",
+ "$1::pg_catalog.int4",
+ 1,
+ 90300
+ },
+ {
+ "lo_truncate",
+ "$1::pg_catalog.int4, $2::pg_catalog.int4",
+ 2,
+ 80300
+ },
+ {
+ "lo_truncate64",
+ "$1::pg_catalog.int4, $2::pg_catalog.int8",
+ 2,
+ 90300
+ },
+ {
+ "loread",
+ "$1::pg_catalog.int4, $2::pg_catalog.int4",
+ 2
+ },
+ {
+ "lowrite",
+ "$1::pg_catalog.int4, $2::pg_catalog.bytea",
+ 2
+ }
+};
+
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);
+}
/*
* lo_open
@@ -57,26 +148,28 @@ 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 argFormats[] = {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;
+
+ mode = pg_hton32(mode);
+ argv[1] = (char *) &mode;
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = mode;
+ res = PQexecPrepared(conn, "libpq_internal_lo_open", 2,
+ (const char *const *) argv, argLens, argFormats, 1);
- res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 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 +188,26 @@ 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 argFormats[] = {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 = PQexecPrepared(conn, "libpq_internal_lo_close", 1,
+ (const char *const *) argv, argLens, argFormats, 1);
+
+ 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 +226,18 @@ lo_close(PGconn *conn, int fd)
int
lo_truncate(PGconn *conn, int fd, size_t len)
{
- PQArgBlock argv[2];
+ char *argv[2];
+ int argLens[] = {4, 4};
+ int argFormats[] = {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 +257,20 @@ 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;
+ len = pg_hton32(len);
+ argv[1] = (char *) &len;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate,
- &retval, &result_len, 1, argv, 2);
+ res = PQexecPrepared(conn, "libpq_internal_lo_truncate", 2,
+ (const char *const *) argv, argLens, argFormats, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 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 +289,36 @@ 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 argFormats[] = {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;
+ fd = pg_hton32(fd);
+ argv[0] = (char *) &fd;
- len = lo_hton64(len);
- argv[1].isint = 0;
- argv[1].len = 8;
- argv[1].u.ptr = (int *) &len;
+ len = pg_hton64(len);
+ argv[1] = (char *) &len;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64,
- &retval, &result_len, 1, argv, 2);
+ res = PQexecPrepared(conn, "ligpq_internal_lo_truncate64", 2,
+ (const char *const *) argv, argLens, argFormats, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 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 +338,10 @@ 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 argLens[] = {4, 4};
+ int argFormats[] = {1, 1};
PGresult *res;
- int result_len;
if (lo_initialize(conn) < 0)
return -1;
@@ -263,18 +358,24 @@ 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;
+
+ len = pg_hton32(len);
+ argv[1] = (char *) &len;
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = (int) len;
+ res = PQexecPrepared(conn, "libpq_internal_loread", 2,
+ (const char *const *) argv, argLens, argFormats, 1);
- res = PQnfn(conn, conn->lobjfuncs->fn_lo_read,
- (void *) buf, len, &result_len, 0, argv, 2);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 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 +395,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 argFormats[] = {1, 1};
PGresult *res;
- int result_len;
int retval;
if (lo_initialize(conn) < 0)
@@ -314,20 +416,19 @@ 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] = unconstify(char *, buf);
- argv[1].isint = 0;
- argv[1].len = (int) len;
- argv[1].u.ptr = (int *) unconstify(char *, buf);
+ res = PQexecPrepared(conn, "libpq_internal_lowrite", 2,
+ (const char *const *) argv, argLens, argFormats, 1);
- res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
- &retval, &result_len, 1, argv, 2);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 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 +444,32 @@ 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 argFormats[] = {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 = PQexecPrepared(conn, "libpq_internal_lseek", 3,
+ (const char *const *) argv, argLens, argFormats, 1);
+
+ 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 +485,38 @@ 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 argFormats[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 = PQexecPrepared(conn, "ligpq_internal_lo_lseek64", 3,
+ (const char *const *) argv, argLens, argFormats, 1);
+ if (lo_result_is_valid(res, sizeof(retval)))
{
+ memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval));
PQclear(res);
- return lo_ntoh64(retval);
+ return pg_ntoh32(retval);
}
else
{
@@ -437,23 +536,26 @@ 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 argFormats[] = {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 = PQexecPrepared(conn, "libpq_internal_lo_creat", 1,
+ (const char *const *) argv, argLens, argFormats, 1);
+
+ 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,33 @@ lo_creat(PGconn *conn, int mode)
Oid
lo_create(PGconn *conn, Oid lobjId)
{
- PQArgBlock argv[1];
+ char *argv[1];
+ int argLens[] = {4};
+ int argFormats[] = {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 = PQexecPrepared(conn, "libpq_internal_lo_create", 1,
+ (const char *const *) argv, argLens, argFormats, 1);
+
+ 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 +619,25 @@ int
lo_tell(PGconn *conn, int fd)
{
int retval;
- PQArgBlock argv[1];
+ char *argv[1];
+ int argLens[] = {4};
+ int argFormats[] = {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 = PQexecPrepared(conn, "libpq_internal_lo_tell", 1,
+ (const char *const *) argv, argLens, argFormats, 1);
+
+ 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 +654,32 @@ int64_t
lo_tell64(PGconn *conn, int fd)
{
int64 retval;
- PQArgBlock argv[1];
+ char *argv[1];
+ int argLens[] = {4};
+ int argFormats[] = {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 = PQexecPrepared(conn, "libpq_internal_lo_tell64", 1,
+ (const char *const *) argv, argLens, argFormats, 1);
- 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)
+ 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 +696,26 @@ lo_tell64(PGconn *conn, int fd)
int
lo_unlink(PGconn *conn, Oid lobjId)
{
- PQArgBlock argv[1];
+ char *argv[1];
+ int argLens[] = {4};
+ int argFormats[] = {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 = PQexecPrepared(conn, "libpq_internal_lo_unlink", 1,
+ (const char *const *) argv, argLens, argFormats, 1);
- res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
- &retval, &result_len, 1, argv, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
+ 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
{
@@ -836,19 +946,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;
@@ -856,209 +959,44 @@ lo_initialize(PGconn *conn)
/* Since this is the beginning of a query cycle, reset the error state */
pqClearConnErrorState(conn);
- /* Nothing else to do if we already collected info */
- if (conn->lobjfuncs != NULL)
+ /* Nothing else to do if we already prepared the statements */
+ 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)
+ for (int i = 0; i < lengthof(lobjfuncs); i++)
{
- libpq_append_conn_error(conn, "out of memory");
- return -1;
- }
- MemSet(lobjfuncs, 0, sizeof(PGlobjfuncs));
+ PGresult *res;
+ ExecStatusType status;
+ PQExpBufferData pname;
+ PQExpBufferData pquery;
- /*
- * 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')";
+ if (PQserverVersion(conn) < lobjfuncs[i].version)
+ continue;
- res = PQexec(conn, query);
- if (res == NULL)
- {
- free(lobjfuncs);
- return -1;
- }
+ initPQExpBuffer(&pname);
+ initPQExpBuffer(&pquery);
- if (res->resultStatus != PGRES_TUPLES_OK)
- {
- free(lobjfuncs);
- PQclear(res);
- libpq_append_conn_error(conn, "query to initialize large object functions did not return data");
- return -1;
- }
+ appendPQExpBuffer(&pname, "libpq_internal_%s",
+ lobjfuncs[i].name);
+ appendPQExpBuffer(&pquery, "SELECT pg_catalog.%s(%s)",
+ lobjfuncs[i].name, lobjfuncs[i].args);
- /*
- * 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;
- }
+ res = PQprepare(conn, pname.data, pquery.data,
+ lobjfuncs[i].nargs, NULL);
+ status = PQresultStatus(res);
+ if (res)
+ PQclear(res);
- PQclear(res);
+ termPQExpBuffer(&pname);
+ termPQExpBuffer(&pquery);
- /*
- * 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);
+ if (status == PGRES_COMMAND_OK)
+ continue;
+
+ libpq_append_conn_error(conn, "query to prepare large object statements failed");
return -1;
}
- /*
- * 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 461b39620c3..5248e895bc0 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.
@@ -562,7 +544,7 @@ 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 */
pg_prng_state prng_state; /* prng state for load balancing connections */
--
2.50.1 (Apple Git-155)
--NDf4SqMA2rj1ZNJ4
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
filename=v1-0002-remove-support-for-PQfn.patch