Thread

  1. [PATCH v2 2/3] stop using PQfn() in libpq's LO interface

    Nathan Bossart <nathan@postgresql.org> — 2026-05-22T17:40:38Z

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