On Tue, May 26, 2026 at 11:36:49PM +0200, Jelte Fennema-Nio wrote:
> Could you share the simple LO test you were running here and/or rerun it
> with this patch applied? I'd love to know if the patch reduces the
> slowdown significantly, or if something else is the bottleneck.

The test is just:

    int main()
    {
        PCconn *conn = PQsetdb(NULL, NULL, NULL, NULL, "postgres");

        for (int i = 0; i < 1000000; i++)
            lo_create(conn, i);

        for (int i = 0; i < 1000000; i++)
            lo_unlink(conn, i);
    }

Before applying your patch -> 0.457 seconds (best of ~10)
After applying your patch  -> 0.444 seconds (best of ~10)

For reference, HEAD runs this test in 0.319 seconds.

I've attached my work-in-progress patches here, in case you're interested.
0001 switches the frontend LO interface to use prepared statements, and
0002 removes PQfn() entirely.  0003 applies on top of those two and
switches the frontend LO interface to use PQexecParams() instead.

-- 
nathan
>From af1f2430f55c753549669d938f70ca58baa48f3e Mon Sep 17 00:00:00 2001
From: Nathan Bossart <[email protected]>
Date: Fri, 22 May 2026 10:40:38 -0700
Subject: [PATCH v1 1/3] stop using PQfn() in libpq's LO interface

---
 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)

>From 4a8c81ac2a700dfebaa944e0de52974ea91f477a Mon Sep 17 00:00:00 2001
From: Nathan Bossart <[email protected]>
Date: Fri, 22 May 2026 12:00:50 -0700
Subject: [PATCH v1 2/3] remove support for PQfn

---
 doc/src/sgml/libpq.sgml             | 119 +-------------
 src/backend/tcop/fastpath.c         |   3 +-
 src/include/tcop/dest.h             |   4 +-
 src/interfaces/libpq/fe-exec.c      |  54 +------
 src/interfaces/libpq/fe-protocol3.c | 242 ----------------------------
 src/interfaces/libpq/libpq-int.h    |   5 -
 6 files changed, 12 insertions(+), 415 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 7d3c3bb66d8..812e9089bfd 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -3738,7 +3738,7 @@ PGresult *PQdescribePrepared(PGconn *conn, const char 
*stmtName);
         <xref linkend="libpq-PQparamtype"/> can be applied to this
         <structname>PGresult</structname> to obtain information about the 
parameters
         of the prepared statement, and the functions
-        <xref linkend="libpq-PQnfields"/>, <xref linkend="libpq-PQfname"/>,
+        <xref linkend="libpq-PQnfields"/>,
         <xref linkend="libpq-PQftype"/>, etc. provide information about the
         result columns (if any) of the statement.
        </para>
@@ -5887,7 +5887,7 @@ int PQflush(PGconn *conn);
     are permitted, command strings containing multiple SQL commands are
     disallowed, and so is <literal>COPY</literal>.
     Using synchronous command execution functions
-    such as <function>PQfn</function>,
+    such as
     <function>PQexec</function>,
     <function>PQexecParams</function>,
     <function>PQprepare</function>,
@@ -7046,121 +7046,6 @@ int PQrequestCancel(PGconn *conn);
   </sect2>
  </sect1>
 
- <sect1 id="libpq-fastpath">
-  <title>The Fast-Path Interface</title>
-
-  <indexterm zone="libpq-fastpath">
-   <primary>fast path</primary>
-  </indexterm>
-
-  <para>
-   <productname>PostgreSQL</productname> provides a fast-path interface
-   to send simple function calls to the server.
-  </para>
-
-  <warning>
-   <para>
-    This interface is unsafe and should not be used.  When
-    <parameter>result_is_int</parameter> is set to <literal>0</literal>,
-    <function>PQfn</function> may write data beyond the end of
-    <parameter>result_buf</parameter>, regardless of whether the buffer has
-    enough space for the requested number of bytes.  Furthermore, it is
-    obsolete, as one can achieve similar
-    performance and greater functionality by setting up a prepared
-    statement to define the function call.  Then, executing the statement
-    with binary transmission of parameters and results substitutes for a
-    fast-path function call.
-   </para>
-  </warning>
-
-  <para>
-   The function <function 
id="libpq-PQfn">PQfn</function><indexterm><primary>PQfn</primary></indexterm>
-   requests execution of a server function via the fast-path interface:
-<synopsis>
-PGresult *PQfn(PGconn *conn,
-               int fnid,
-               int *result_buf,
-               int *result_len,
-               int result_is_int,
-               const PQArgBlock *args,
-               int nargs);
-
-typedef struct
-{
-    int len;
-    int isint;
-    union
-    {
-        int *ptr;
-        int integer;
-    } u;
-} PQArgBlock;
-</synopsis>
-  </para>
-
-  <para>
-   The <parameter>fnid</parameter> argument is the OID of the function to be
-   executed.  <parameter>args</parameter> and <parameter>nargs</parameter> 
define the
-   parameters to be passed to the function; they must match the declared
-   function argument list.  When the <parameter>isint</parameter> field of a
-   parameter structure is true, the <parameter>u.integer</parameter> value is 
sent
-   to the server as an integer of the indicated length (this must be
-   2 or 4 bytes); proper byte-swapping occurs.  When 
<parameter>isint</parameter>
-   is false, the indicated number of bytes at <parameter>*u.ptr</parameter> are
-   sent with no processing; the data must be in the format expected by
-   the server for binary transmission of the function's argument data
-   type.  (The declaration of <parameter>u.ptr</parameter> as being of
-   type <type>int *</type> is historical; it would be better to consider
-   it <type>void *</type>.)
-   <parameter>result_buf</parameter> points to the buffer in which to place
-   the function's return value.  The caller must have allocated sufficient
-   space to store the return value.  (There is no check!) The actual result
-   length in bytes will be returned in the integer pointed to by
-   <parameter>result_len</parameter>.  If a 2- or 4-byte integer result
-   is expected, set <parameter>result_is_int</parameter> to 1, otherwise
-   set it to 0.  Setting <parameter>result_is_int</parameter> to 1 causes
-   <application>libpq</application> to byte-swap the value if necessary, so 
that it
-   is delivered as a proper <type>int</type> value for the client machine;
-   note that a 4-byte integer is delivered into 
<parameter>*result_buf</parameter>
-   for either allowed result size.
-   When <parameter>result_is_int</parameter> is 0, the binary-format byte 
string
-   sent by the server is returned unmodified. (In this case it's better
-   to consider <parameter>result_buf</parameter> as being of
-   type <type>void *</type>.)
-  </para>
-
-  <para>
-   <function>PQfn</function> always returns a valid
-   <structname>PGresult</structname> pointer, with
-   status <literal>PGRES_COMMAND_OK</literal> for success
-   or <literal>PGRES_FATAL_ERROR</literal> if some problem was encountered.
-   The result status should be
-   checked before the result is used.   The caller is responsible for
-   freeing  the  <structname>PGresult</structname>  with
-   <xref linkend="libpq-PQclear"/> when it is no longer needed.
-  </para>
-
-  <para>
-   To pass a NULL argument to the function, set
-   the <parameter>len</parameter> field of that parameter structure
-   to <literal>-1</literal>; the <parameter>isint</parameter>
-   and <parameter>u</parameter> fields are then irrelevant.
-  </para>
-
-  <para>
-   If the function returns NULL, <parameter>*result_len</parameter> is set
-   to <literal>-1</literal>, and <parameter>*result_buf</parameter> is not
-   modified.
-  </para>
-
-  <para>
-   Note that it is not possible to handle set-valued results when using
-   this interface.  Also, the function must be a plain function, not an
-   aggregate, window function, or procedure.
-  </para>
-
- </sect1>
-
  <sect1 id="libpq-notify">
   <title>Asynchronous Notification</title>
 
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 52772bc90a8..2a5c45efffe 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -11,7 +11,8 @@
  *       src/backend/tcop/fastpath.c
  *
  * NOTES
- *       This cruft is the server side of PQfn.
+ *       This cruft is the server side of PQfn, which was removed in v20 but
+ *       may still be used by older clients.
  *
  *-------------------------------------------------------------------------
  */
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 103f27fc3cb..507414421ec 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -12,8 +12,8 @@
  *
  *       - a remote process is the destination when we are
  *             running a backend with a frontend and the frontend executes
- *             PQexec() or PQfn().  In this case, the results are sent
- *             to the frontend via the functions in backend/libpq.
+ *             PQexec().  In this case, the results are sent to the frontend 
via
+ *             the functions in backend/libpq.
  *
  *       - DestNone is the destination when the system executes
  *             a query internally.  The results are discarded.
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 7b8edacbfde..400e1eef94b 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -2986,13 +2986,11 @@ PQendcopy(PGconn *conn)
  *             nargs                   : # of arguments in args array.
  *
  * RETURNS
- *             PGresult with status = PGRES_COMMAND_OK if successful.
- *                     *result_len is > 0 if there is a return value, 0 if not.
- *             PGresult with status = PGRES_FATAL_ERROR if backend returns an 
error.
- *             NULL on communications failure.  conn->errorMessage will be set.
+ *             This function was unsafe and is no longer supported, so it now 
always
+ *             sets *result_len to 0 and returns a PGresult with status set to
+ *             PGRES_FATAL_ERROR.
  * ----------------
  */
-
 PGresult *
 PQfn(PGconn *conn,
         int fnid,
@@ -3001,51 +2999,11 @@ PQfn(PGconn *conn,
         int result_is_int,
         const PQArgBlock *args,
         int nargs)
-{
-       return PQnfn(conn, fnid, result_buf, -1, result_len,
-                                result_is_int, args, nargs);
-}
-
-/*
- * PQnfn
- *             Private version of PQfn() with verification that returned data 
fits in
- *             result_buf when result_is_int == 0.  Setting buf_size to -1 
disables
- *             this verification.
- */
-PGresult *
-PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len,
-         int result_is_int, const PQArgBlock *args, int nargs)
 {
        *result_len = 0;
-
-       if (!conn)
-               return NULL;
-
-       /*
-        * Since this is the beginning of a query cycle, reset the error state.
-        * However, in pipeline mode with something already queued, the error
-        * buffer belongs to that command and we shouldn't clear it.
-        */
-       if (conn->cmd_queue_head == NULL)
-               pqClearConnErrorState(conn);
-
-       if (conn->pipelineStatus != PQ_PIPELINE_OFF)
-       {
-               libpq_append_conn_error(conn, "%s not allowed in pipeline 
mode", "PQfn");
-               return NULL;
-       }
-
-       if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE 
||
-               pgHavePendingResult(conn))
-       {
-               libpq_append_conn_error(conn, "connection in wrong state");
-               return NULL;
-       }
-
-       return pqFunctionCall3(conn, fnid,
-                                                  result_buf, buf_size, 
result_len,
-                                                  result_is_int,
-                                                  args, nargs);
+       libpq_append_conn_error(conn, "PQfn() is no longer supported");
+       pqSaveErrorResult(conn);
+       return pqPrepareAsyncResult(conn);
 }
 
 /* ====== Pipeline mode support ======== */
diff --git a/src/interfaces/libpq/fe-protocol3.c 
b/src/interfaces/libpq/fe-protocol3.c
index 840e018cd18..08910a459c0 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -2196,248 +2196,6 @@ pqEndcopy3(PGconn *conn)
        return 1;
 }
 
-
-/*
- * PQfn - Send a function call to the POSTGRES backend.
- *
- * See fe-exec.c for documentation.
- */
-PGresult *
-pqFunctionCall3(PGconn *conn, Oid fnid,
-                               int *result_buf, int buf_size, int 
*actual_result_len,
-                               int result_is_int,
-                               const PQArgBlock *args, int nargs)
-{
-       bool            needInput = false;
-       ExecStatusType status = PGRES_FATAL_ERROR;
-       char            id;
-       int                     msgLength;
-       int                     avail;
-       int                     i;
-
-       /* already validated by PQfn */
-       Assert(conn->pipelineStatus == PQ_PIPELINE_OFF);
-
-       /* PQfn already validated connection state */
-
-       if (pqPutMsgStart(PqMsg_FunctionCall, conn) < 0 ||
-               pqPutInt(fnid, 4, conn) < 0 ||  /* function id */
-               pqPutInt(1, 2, conn) < 0 || /* # of format codes */
-               pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */
-               pqPutInt(nargs, 2, conn) < 0)   /* # of args */
-       {
-               /* error message should be set up already */
-               return NULL;
-       }
-
-       for (i = 0; i < nargs; ++i)
-       {                                                       /* len.int4 + 
contents     */
-               if (pqPutInt(args[i].len, 4, conn))
-                       return NULL;
-               if (args[i].len == -1)
-                       continue;                       /* it's NULL */
-
-               if (args[i].isint)
-               {
-                       if (pqPutInt(args[i].u.integer, args[i].len, conn))
-                               return NULL;
-               }
-               else
-               {
-                       if (pqPutnchar(args[i].u.ptr, args[i].len, conn))
-                               return NULL;
-               }
-       }
-
-       if (pqPutInt(1, 2, conn) < 0)   /* result format code: BINARY */
-               return NULL;
-
-       if (pqPutMsgEnd(conn) < 0 ||
-               pqFlush(conn))
-               return NULL;
-
-       for (;;)
-       {
-               if (needInput)
-               {
-                       /* Wait for some data to arrive (or for the channel to 
close) */
-                       if (pqWait(true, false, conn) ||
-                               pqReadData(conn) < 0)
-                               break;
-               }
-
-               /*
-                * Scan the message. If we run out of data, loop around to try 
again.
-                */
-               needInput = true;
-
-               conn->inCursor = conn->inStart;
-               if (pqGetc(&id, conn))
-                       continue;
-               if (pqGetInt(&msgLength, 4, conn))
-                       continue;
-
-               /*
-                * Try to validate message type/length here.  A length less 
than 4 is
-                * definitely broken.  Large lengths should only be believed 
for a few
-                * message types.
-                */
-               if (msgLength < 4)
-               {
-                       handleSyncLoss(conn, id, msgLength);
-                       break;
-               }
-               if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id))
-               {
-                       handleSyncLoss(conn, id, msgLength);
-                       break;
-               }
-
-               /*
-                * Can't process if message body isn't all here yet.
-                */
-               msgLength -= 4;
-               avail = conn->inEnd - conn->inCursor;
-               if (avail < msgLength)
-               {
-                       /*
-                        * Before looping, enlarge the input buffer if needed 
to hold the
-                        * whole message.  See notes in parseInput.
-                        */
-                       if (pqCheckInBufferSpace(conn->inCursor + (size_t) 
msgLength,
-                                                                        conn))
-                       {
-                               /*
-                                * Abandon the connection.  There's not much 
else we can
-                                * safely do; we can't just ignore the message 
or we could
-                                * miss important changes to the connection 
state.
-                                * pqCheckInBufferSpace() already reported the 
error.
-                                */
-                               handleFatalError(conn);
-                               break;
-                       }
-                       continue;
-               }
-
-               /*
-                * We should see V or E response to the command, but might get N
-                * and/or A notices first. We also need to swallow the final Z 
before
-                * returning.
-                */
-               switch (id)
-               {
-                       case PqMsg_FunctionCallResponse:
-                               if (pqGetInt(actual_result_len, 4, conn))
-                                       continue;
-                               if (*actual_result_len != -1)
-                               {
-                                       if (result_is_int)
-                                       {
-                                               if (pqGetInt(result_buf, 
*actual_result_len, conn))
-                                                       continue;
-                                       }
-                                       else
-                                       {
-                                               /*
-                                                * If the server returned too 
much data for the
-                                                * buffer, something fishy is 
going on.  Abandon ship.
-                                                */
-                                               if (buf_size != -1 && 
*actual_result_len > buf_size)
-                                               {
-                                                       
libpq_append_conn_error(conn, "server returned too much data");
-                                                       handleFatalError(conn);
-                                                       return 
pqPrepareAsyncResult(conn);
-                                               }
-
-                                               if (pqGetnchar(result_buf,
-                                                                          
*actual_result_len,
-                                                                          
conn))
-                                                       continue;
-                                       }
-                               }
-                               /* correctly finished function result message */
-                               status = PGRES_COMMAND_OK;
-                               break;
-                       case PqMsg_ErrorResponse:
-                               if (pqGetErrorNotice3(conn, true))
-                                       continue;
-                               status = PGRES_FATAL_ERROR;
-                               break;
-                       case PqMsg_NotificationResponse:
-                               /* handle notify and go back to processing 
return values */
-                               if (getNotify(conn))
-                                       continue;
-                               break;
-                       case PqMsg_NoticeResponse:
-                               /* handle notice and go back to processing 
return values */
-                               if (pqGetErrorNotice3(conn, false))
-                                       continue;
-                               break;
-                       case PqMsg_ReadyForQuery:
-                               if (getReadyForQuery(conn))
-                                       continue;
-
-                               /* consume the message */
-                               pqParseDone(conn, conn->inStart + 5 + 
msgLength);
-
-                               /*
-                                * If we already have a result object (probably 
an error), use
-                                * that.  Otherwise, if we saw a function 
result message,
-                                * report COMMAND_OK.  Otherwise, the backend 
violated the
-                                * protocol, so complain.
-                                */
-                               if (!pgHavePendingResult(conn))
-                               {
-                                       if (status == PGRES_COMMAND_OK)
-                                       {
-                                               conn->result = 
PQmakeEmptyPGresult(conn, status);
-                                               if (!conn->result)
-                                               {
-                                                       
libpq_append_conn_error(conn, "out of memory");
-                                                       pqSaveErrorResult(conn);
-                                               }
-                                       }
-                                       else
-                                       {
-                                               libpq_append_conn_error(conn, 
"protocol error: no function result");
-                                               pqSaveErrorResult(conn);
-                                       }
-                               }
-                               /* and we're out */
-                               return pqPrepareAsyncResult(conn);
-                       case PqMsg_ParameterStatus:
-                               if (getParameterStatus(conn))
-                                       continue;
-                               break;
-                       default:
-                               /* The backend violates the protocol. */
-                               libpq_append_conn_error(conn, "protocol error: 
id=0x%x", id);
-                               pqSaveErrorResult(conn);
-
-                               /*
-                                * We can't call parsing done due to the 
protocol violation
-                                * (so message tracing wouldn't work), but 
trust the specified
-                                * message length as what to skip.
-                                */
-                               conn->inStart += 5 + msgLength;
-                               return pqPrepareAsyncResult(conn);
-               }
-
-               /* Completed parsing this message, keep going */
-               pqParseDone(conn, conn->inStart + 5 + msgLength);
-               needInput = false;
-       }
-
-       /*
-        * We fall out of the loop only upon failing to read data.
-        * conn->errorMessage has been set by pqWait or pqReadData. We want to
-        * append it to any already-received error message.
-        */
-       pqSaveErrorResult(conn);
-       return pqPrepareAsyncResult(conn);
-}
-
-
 /*
  * Construct startup packet
  *
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 5248e895bc0..9dd0f42b6b7 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -764,11 +764,6 @@ extern int pqGetCopyData3(PGconn *conn, char **buffer, int 
async);
 extern int     pqGetline3(PGconn *conn, char *s, int maxlen);
 extern int     pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
 extern int     pqEndcopy3(PGconn *conn);
-extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
-                                                                int 
*result_buf, int buf_size,
-                                                                int 
*actual_result_len,
-                                                                int 
result_is_int,
-                                                                const 
PQArgBlock *args, int nargs);
 
 /* === in fe-cancel.c === */
 
-- 
2.50.1 (Apple Git-155)

>From e5823beb79599bf17ba45f19fcca54c75f85c82d Mon Sep 17 00:00:00 2001
From: Nathan Bossart <[email protected]>
Date: Tue, 26 May 2026 11:12:49 -0500
Subject: [PATCH v1 3/3] LO frontend PQexecParams

---
 src/interfaces/libpq/fe-connect.c |   1 -
 src/interfaces/libpq/fe-lobj.c    | 236 ++++--------------------------
 src/interfaces/libpq/libpq-int.h  |   1 -
 3 files changed, 26 insertions(+), 212 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c 
b/src/interfaces/libpq/fe-connect.c
index 01c9735f276..9f6aa01dc0e 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -692,7 +692,6 @@ pqDropServerData(PGconn *conn)
        conn->in_hot_standby = PG_BOOL_UNKNOWN;
        conn->scram_sha_256_iterations = SCRAM_SHA_256_DEFAULT_ITERATIONS;
        conn->sversion = 0;
-       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 41573c22c80..17c1d699a48 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -41,89 +41,6 @@
 
 #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 bool
@@ -153,17 +70,14 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
        int                     argFormats[] = {1, 1};
        PGresult   *res;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        lobjId = pg_hton32(lobjId);
        argv[0] = (char *) &lobjId;
 
        mode = pg_hton32(mode);
        argv[1] = (char *) &mode;
 
-       res = PQexecPrepared(conn, "libpq_internal_lo_open", 2,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT pg_catalog.lo_open($1::pg_catalog.oid, 
$2::pg_catalog.int4)", 2, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(fd)))
        {
@@ -194,14 +108,11 @@ lo_close(PGconn *conn, int fd)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        fd = pg_hton32(fd);
        argv[0] = (char *) &fd;
 
-       res = PQexecPrepared(conn, "libpq_internal_lo_close", 1,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_close($1::pg_catalog.int4)", 1, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -232,9 +143,6 @@ lo_truncate(PGconn *conn, int fd, size_t len)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        if (PQserverVersion(conn) < 80300)
        {
                libpq_append_conn_error(conn, "server does not support function 
\"%s\"",
@@ -263,8 +171,8 @@ lo_truncate(PGconn *conn, int fd, size_t len)
        len = pg_hton32(len);
        argv[1] = (char *) &len;
 
-       res = PQexecPrepared(conn, "libpq_internal_lo_truncate", 2,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_truncate($1::pg_catalog.int4, $2::pg_catalog.int4)", 2, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -295,9 +203,6 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        if (PQserverVersion(conn) < 90300)
        {
                libpq_append_conn_error(conn, "server does not support function 
\"%s\"",
@@ -311,8 +216,8 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
        len = pg_hton64(len);
        argv[1] = (char *) &len;
 
-       res = PQexecPrepared(conn, "ligpq_internal_lo_truncate64", 2,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_truncate64($1::pg_catalog.int4, $2::pg_catalog.int8)", 2, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -343,9 +248,6 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
        int                     argFormats[] = {1, 1};
        PGresult   *res;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        /*
         * Long ago, somebody thought it'd be a good idea to declare this 
function
         * as taking size_t ... but the underlying backend function only 
accepts a
@@ -364,8 +266,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
        len = pg_hton32(len);
        argv[1] = (char *) &len;
 
-       res = PQexecPrepared(conn, "libpq_internal_loread", 2,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT pg_catalog.loread($1::pg_catalog.int4, 
$2::pg_catalog.int4)", 2, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, -1))
        {
@@ -401,9 +303,6 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        /*
         * Long ago, somebody thought it'd be a good idea to declare this 
function
         * as taking size_t ... but the underlying backend function only 
accepts a
@@ -421,8 +320,8 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
 
        argv[1] = unconstify(char *, buf);
 
-       res = PQexecPrepared(conn, "libpq_internal_lowrite", 2,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lowrite($1::pg_catalog.int4, $2::pg_catalog.bytea)", 2, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -450,9 +349,6 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        fd = pg_hton32(fd);
        argv[0] = (char *) &fd;
 
@@ -462,8 +358,8 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
        whence = pg_hton32(whence);
        argv[2] = (char *) &whence;
 
-       res = PQexecPrepared(conn, "libpq_internal_lseek", 3,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT pg_catalog.lseek($1::pg_catalog.int4, 
$2::pg_catalog.int4, $3::pg_catalog.int4)", 3, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -491,9 +387,6 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
        PGresult   *res;
        int64           retval;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        if (PQserverVersion(conn) < 90300)
        {
                libpq_append_conn_error(conn, "server does not support function 
\"%s\"",
@@ -510,8 +403,8 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
        whence = pg_hton32(whence);
        argv[2] = (char *) &whence;
 
-       res = PQexecPrepared(conn, "ligpq_internal_lo_lseek64", 3,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_lseek64($1::pg_catalog.int4, $2::pg_catalog.int8, 
$3::pg_catalog.int4)", 3, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
        if (lo_result_is_valid(res, sizeof(retval)))
        {
                memcpy(&retval, PQgetvalue(res, 0, 0), sizeof(retval));
@@ -542,14 +435,11 @@ lo_creat(PGconn *conn, int mode)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return InvalidOid;
-
        mode = pg_hton32(mode);
        argv[0] = (char *) &mode;
 
-       res = PQexecPrepared(conn, "libpq_internal_lo_creat", 1,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_creat($1::pg_catalog.int4)", 1, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -581,9 +471,6 @@ lo_create(PGconn *conn, Oid lobjId)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return InvalidOid;
-
        if (PQserverVersion(conn) < 80100)
        {
                libpq_append_conn_error(conn, "server does not support function 
\"%s\"",
@@ -594,8 +481,8 @@ lo_create(PGconn *conn, Oid lobjId)
        lobjId = pg_hton32(lobjId);
        argv[0] = (char *) &lobjId;
 
-       res = PQexecPrepared(conn, "libpq_internal_lo_create", 1,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_create($1::pg_catalog.oid)", 1, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -624,14 +511,11 @@ lo_tell(PGconn *conn, int fd)
        int                     argFormats[] = {1};
        PGresult   *res;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        fd = pg_hton32(fd);
        argv[0] = (char *) &fd;
 
-       res = PQexecPrepared(conn, "libpq_internal_lo_tell", 1,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_tell($1::pg_catalog.int4)", 1, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -659,9 +543,6 @@ lo_tell64(PGconn *conn, int fd)
        int                     argFormats[] = {1};
        PGresult   *res;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        if (PQserverVersion(conn) < 90300)
        {
                libpq_append_conn_error(conn, "server does not support 
functions \"%s\"",
@@ -672,8 +553,8 @@ lo_tell64(PGconn *conn, int 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 = PQexecParams(conn, "SELECT 
pg_catalog.lo_tell64($1::pg_catalog.int4)", 1, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -702,14 +583,11 @@ lo_unlink(PGconn *conn, Oid lobjId)
        PGresult   *res;
        int                     retval;
 
-       if (lo_initialize(conn) < 0)
-               return -1;
-
        lobjId = pg_hton32(lobjId);
        argv[0] = (char *) &lobjId;
 
-       res = PQexecPrepared(conn, "libpq_internal_lo_unlink", 1,
-                                                (const char *const *) argv, 
argLens, argFormats, 1);
+       res = PQexecParams(conn, "SELECT 
pg_catalog.lo_unlink($1::pg_catalog.oid)", 1, NULL,
+                                          (const char *const *) argv, argLens, 
argFormats, 1);
 
        if (lo_result_is_valid(res, sizeof(retval)))
        {
@@ -938,65 +816,3 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename)
 
        return result;
 }
-
-
-/*
- * lo_initialize
- *
- * 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 prepared statements for all the
- * functions that are required for large object operations.
- */
-static int
-lo_initialize(PGconn *conn)
-{
-       /* Nothing we can do with no connection */
-       if (conn == NULL)
-               return -1;
-
-       /* Since this is the beginning of a query cycle, reset the error state 
*/
-       pqClearConnErrorState(conn);
-
-       /* Nothing else to do if we already prepared the statements */
-       if (conn->lobjprepared)
-               return 0;
-
-       for (int i = 0; i < lengthof(lobjfuncs); i++)
-       {
-               PGresult   *res;
-               ExecStatusType status;
-               PQExpBufferData pname;
-               PQExpBufferData pquery;
-
-               if (PQserverVersion(conn) < lobjfuncs[i].version)
-                       continue;
-
-               initPQExpBuffer(&pname);
-               initPQExpBuffer(&pquery);
-
-               appendPQExpBuffer(&pname, "libpq_internal_%s",
-                                                 lobjfuncs[i].name);
-               appendPQExpBuffer(&pquery, "SELECT pg_catalog.%s(%s)",
-                                                 lobjfuncs[i].name, 
lobjfuncs[i].args);
-
-               res = PQprepare(conn, pname.data, pquery.data,
-                                               lobjfuncs[i].nargs, NULL);
-               status = PQresultStatus(res);
-               if (res)
-                       PQclear(res);
-
-               termPQExpBuffer(&pname);
-               termPQExpBuffer(&pquery);
-
-               if (status == PGRES_COMMAND_OK)
-                       continue;
-
-               libpq_append_conn_error(conn, "query to prepare large object 
statements failed");
-               return -1;
-       }
-
-       conn->lobjprepared = true;
-       return 0;
-}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 9dd0f42b6b7..cf968f1519b 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -544,7 +544,6 @@ struct pg_conn
        PGTernaryBool in_hot_standby;   /* in_hot_standby */
        PGVerbosity verbosity;          /* error/notice message verbosity */
        PGContextVisibility show_context;       /* whether to show CONTEXT 
field */
-       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)

Reply via email to