agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feedFrom: Nathan Bossart <nathandbossart@gmail.com>
To: Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>
Cc: Jacob Champion <jacob.champion@enterprisedb.com>
Cc: pgsql-hackers@postgresql.org
Subject: Re: future of PQfn()
Date: Thu, 4 Jun 2026 09:57:47 -0500
Message-ID: <aiGSa7XpUTRFt-be@nathan> (raw)
In-Reply-To: <87ldcu1tfd.fsf@wibble.ilmari.org>
References: <ahX6nb1aqh51IhHC@nathan>
<CAOYmi+n6eFXs_pncObs9Vhe7S1gbvepRncYJaaO98anxL8WODg@mail.gmail.com>
<ahmtVYcf4EYwz02U@nathan>
<CAOYmi+mA4Cdb+RWV2M83FbsSN85t2iuoXejA-PKNrQ_ayHOQWw@mail.gmail.com>
<ahm6kuL3L3wSYo6d@nathan>
<CAOYmi+nhtqGZAgtLvR2OdNZ8mfdY79qseEtKXnbs1jvtLNOrZA@mail.gmail.com>
<ahnB8zuR0AYegcJI@nathan>
<CAOYmi+=sO6dR6P2c48f6ZT9aOgzVdf+pjn3TMsehTPr8-Yxjsw@mail.gmail.com>
<aiCeYhW4Ksu-BFqC@nathan>
<87ldcu1tfd.fsf@wibble.ilmari.org>
On Thu, Jun 04, 2026 at 11:39:02AM +0100, Dagfinn Ilmari Mannsåker wrote:
> Nathan Bossart <nathandbossart@gmail.com> writes:
>
>> - <sect1 id="libpq-fastpath">
>> - <title>The Fast-Path Interface</title>
>> -
>> - <indexterm zone="libpq-fastpath">
>> - <primary>fast path</primary>
>> - </indexterm>
>
> Should we move this to the "Obsolete or Renamed Features" appendix
> (e.g. appendix-obsolete-libpq-fastpath.sgml) with a description of why
> it was removed?
Yes, I think we should. Done in the attached.
--
nathan
From 63486371b3531be66bb8e0c62ef1b8cc7e95abb5 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Mon, 1 Jun 2026 16:55:00 -0500
Subject: [PATCH v4 1/3] remove PQfn
---
.../appendix-obsolete-libpq-fastpath.sgml | 24 ++++
doc/src/sgml/appendix-obsolete.sgml | 1 +
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/libpq.sgml | 117 +-----------------
src/backend/tcop/fastpath.c | 4 +-
src/include/tcop/dest.h | 4 +-
src/interfaces/libpq/fe-exec.c | 42 +++++--
src/interfaces/libpq/fe-lobj.c | 38 +++---
8 files changed, 86 insertions(+), 145 deletions(-)
create mode 100644 doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml
diff --git a/doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml b/doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml
new file mode 100644
index 00000000000..e42c2ece487
--- /dev/null
+++ b/doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml -->
+<!--
+ See doc/src/sgml/appendix-obsolete.sgml for why this file exists. Do not change the id attribute.
+-->
+
+<sect1 id="libpq-fastpath">
+ <title><application>libpq</application> Fast-Path Interface Removed</title>
+
+ <indexterm zone="libpq-fastpath">
+ <primary>fast path</primary>
+ </indexterm>
+
+ <para>
+ In <productname>PostgreSQL</productname> 19 and below,
+ <application>libpq</application> supported a fast-path interface to send
+ simple function calls to the server. This interface was unsafe and
+ obsolete, and thus was removed in <productname>PostgreSQL</productname> 20.
+ 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>
+
+</sect1>
diff --git a/doc/src/sgml/appendix-obsolete.sgml b/doc/src/sgml/appendix-obsolete.sgml
index cc002653052..11a033112ef 100644
--- a/doc/src/sgml/appendix-obsolete.sgml
+++ b/doc/src/sgml/appendix-obsolete.sgml
@@ -39,5 +39,6 @@
&obsolete-pgresetxlog;
&obsolete-pgreceivexlog;
&obsolete-auth-radius;
+ &obsolete-libpq-fastpath;
</appendix>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 25a85082759..7dbe741d729 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -209,3 +209,4 @@
<!ENTITY obsolete-pgresetxlog SYSTEM "appendix-obsolete-pgresetxlog.sgml">
<!ENTITY obsolete-pgreceivexlog SYSTEM "appendix-obsolete-pgreceivexlog.sgml">
<!ENTITY obsolete-auth-radius SYSTEM "appendix-obsolete-auth-radius.sgml">
+<!ENTITY obsolete-libpq-fastpath SYSTEM "appendix-obsolete-libpq-fastpath.sgml">
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 7d3c3bb66d8..123e7f03902 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -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..5379e4ad9f5 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -11,7 +11,9 @@
* src/backend/tcop/fastpath.c
*
* NOTES
- * This cruft is the server side of PQfn.
+ * This cruft is the server side of PQfn. libpq's PQfn() was retired in
+ * v20 and now always errors, but the server code is retained for the
+ * benefit of 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..2f034d70e65 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -2986,10 +2986,10 @@ 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 (unless the connection is in the wrong state, in
+ * which case it returns NULL).
* ----------------
*/
@@ -3002,15 +3002,43 @@ PQfn(PGconn *conn,
const PQArgBlock *args,
int nargs)
{
- return PQnfn(conn, fnid, result_buf, -1, result_len,
- result_is_int, args, 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;
+ }
+
+ libpq_append_conn_error(conn, "PQfn() is no longer supported; use a prepared statement or PQexecParams() with binary results instead");
+ pqSaveErrorResult(conn);
+ return pqPrepareAsyncResult(conn);
}
/*
* 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.
+ * this verification. This is currently only used by the frontend LO
+ * interface and will hopefully be removed down the road.
*/
PGresult *
PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len,
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 12a32fcbaf3..1660c969f58 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -72,7 +72,7 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
argv[1].len = 4;
argv[1].u.integer = mode;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_open, &fd, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -106,8 +106,8 @@ lo_close(PGconn *conn, int fd)
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);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_close,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -169,8 +169,8 @@ lo_truncate(PGconn *conn, int fd, size_t len)
argv[1].len = 4;
argv[1].u.integer = (int) len;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate,
- &retval, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate,
+ &retval, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
@@ -218,8 +218,8 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
argv[1].len = 8;
argv[1].u.ptr = (int *) &len;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64,
- &retval, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate64,
+ &retval, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
@@ -322,8 +322,8 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
argv[1].len = (int) len;
argv[1].u.ptr = (int *) unconstify(char *, buf);
- res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
- &retval, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_write,
+ &retval, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -363,8 +363,8 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
argv[2].len = 4;
argv[2].u.integer = whence;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek,
- &retval, &result_len, 1, argv, 3);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek,
+ &retval, -1, &result_len, 1, argv, 3);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -448,8 +448,8 @@ lo_creat(PGconn *conn, int mode)
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);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_creat,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -492,8 +492,8 @@ lo_create(PGconn *conn, Oid lobjId)
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);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_create,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -526,8 +526,8 @@ lo_tell(PGconn *conn, int fd)
argv[0].len = 4;
argv[0].u.integer = fd;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_tell,
- &retval, &result_len, 1, argv, 1);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -600,8 +600,8 @@ lo_unlink(PGconn *conn, Oid lobjId)
argv[0].len = 4;
argv[0].u.integer = lobjId;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
- &retval, &result_len, 1, argv, 1);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_unlink,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
--
2.50.1 (Apple Git-155)
From 181028cbf820700f104f48dfc0e94d0c7f5e42e0 Mon Sep 17 00:00:00 2001
From: Reviewer <review@example.com>
Date: Tue, 2 Jun 2026 17:22:48 +0000
Subject: [PATCH v4 2/3] remove lo_hton64() and lo_ntoh64()
---
src/interfaces/libpq/fe-lobj.c | 58 +++-------------------------------
1 file changed, 4 insertions(+), 54 deletions(-)
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 1660c969f58..42b2a36bda9 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -43,8 +43,6 @@
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);
/*
* lo_open
@@ -213,7 +211,7 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
argv[0].len = 4;
argv[0].u.integer = fd;
- len = lo_hton64(len);
+ len = pg_hton64(len);
argv[1].isint = 0;
argv[1].len = 8;
argv[1].u.ptr = (int *) &len;
@@ -403,7 +401,7 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
argv[0].len = 4;
argv[0].u.integer = fd;
- offset = lo_hton64(offset);
+ offset = pg_hton64(offset);
argv[1].isint = 0;
argv[1].len = 8;
argv[1].u.ptr = (int *) &offset;
@@ -417,7 +415,7 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
{
PQclear(res);
- return lo_ntoh64(retval);
+ return pg_ntoh64(retval);
}
else
{
@@ -571,7 +569,7 @@ lo_tell64(PGconn *conn, int fd)
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
{
PQclear(res);
- return lo_ntoh64(retval);
+ return pg_ntoh64(retval);
}
else
{
@@ -1014,51 +1012,3 @@ lo_initialize(PGconn *conn)
conn->lobjfuncs = lobjfuncs;
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;
-}
--
2.50.1 (Apple Git-155)
From 8e5a937d5e302a56ff2118ce3ea4f69e7db5ae9f Mon Sep 17 00:00:00 2001
From: Reviewer <review@example.com>
Date: Tue, 2 Jun 2026 18:24:30 +0000
Subject: [PATCH v4 3/3] add helper functions for fast-path arg setup
---
src/interfaces/libpq/fe-lobj.c | 110 ++++++++++++---------------------
1 file changed, 38 insertions(+), 72 deletions(-)
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 42b2a36bda9..e8f6d958e84 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -44,6 +44,22 @@
static int lo_initialize(PGconn *conn);
static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid);
+static inline void
+lo_set_int_arg(PQArgBlock *arg, int value)
+{
+ arg->isint = 1;
+ arg->len = 4;
+ arg->u.integer = value;
+}
+
+static inline void
+lo_set_ptr_arg(PQArgBlock *arg, const void *ptr, int len)
+{
+ arg->isint = 0;
+ arg->len = len;
+ arg->u.ptr = (int *) unconstify(void *, ptr);
+}
+
/*
* lo_open
* opens an existing large object
@@ -62,13 +78,8 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = mode;
+ lo_set_int_arg(&argv[0], lobjId);
+ lo_set_int_arg(&argv[1], mode);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_open, &fd, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -101,9 +112,7 @@ lo_close(PGconn *conn, int fd)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_close,
&retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -159,13 +168,8 @@ lo_truncate(PGconn *conn, int fd, size_t len)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = (int) len;
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_int_arg(&argv[1], (int) len);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate,
&retval, -1, &result_len, 1, argv, 2);
@@ -207,14 +211,10 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
len = pg_hton64(len);
- argv[1].isint = 0;
- argv[1].len = 8;
- argv[1].u.ptr = (int *) &len;
+ lo_set_ptr_arg(&argv[1], &len, 8);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate64,
&retval, -1, &result_len, 1, argv, 2);
@@ -261,13 +261,8 @@ 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;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = (int) len;
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_int_arg(&argv[1], (int) len);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_read,
(void *) buf, len, &result_len, 0, argv, 2);
@@ -312,13 +307,8 @@ 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;
-
- argv[1].isint = 0;
- argv[1].len = (int) len;
- argv[1].u.ptr = (int *) unconstify(char *, buf);
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_ptr_arg(&argv[1], buf, (int) len);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_write,
&retval, -1, &result_len, 1, argv, 2);
@@ -349,17 +339,9 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = offset;
-
- argv[2].isint = 1;
- argv[2].len = 4;
- argv[2].u.integer = whence;
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_int_arg(&argv[1], offset);
+ lo_set_int_arg(&argv[2], whence);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek,
&retval, -1, &result_len, 1, argv, 3);
@@ -397,18 +379,12 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
offset = pg_hton64(offset);
- argv[1].isint = 0;
- argv[1].len = 8;
- argv[1].u.ptr = (int *) &offset;
+ lo_set_ptr_arg(&argv[1], &offset, 8);
- argv[2].isint = 1;
- argv[2].len = 4;
- argv[2].u.integer = whence;
+ lo_set_int_arg(&argv[2], whence);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek64,
(void *) &retval, sizeof(retval), &result_len, 0, argv, 3);
@@ -443,9 +419,7 @@ lo_creat(PGconn *conn, int mode)
if (lo_initialize(conn) < 0)
return InvalidOid;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = mode;
+ lo_set_int_arg(&argv[0], mode);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_creat,
&retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -487,9 +461,7 @@ lo_create(PGconn *conn, Oid lobjId)
return InvalidOid;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
+ lo_set_int_arg(&argv[0], lobjId);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_create,
&retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -520,9 +492,7 @@ lo_tell(PGconn *conn, int fd)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell,
&retval, -1, &result_len, 1, argv, 1);
@@ -560,9 +530,7 @@ lo_tell64(PGconn *conn, int fd)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell64,
(void *) &retval, sizeof(retval), &result_len, 0, argv, 1);
@@ -594,9 +562,7 @@ lo_unlink(PGconn *conn, Oid lobjId)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
+ lo_set_int_arg(&argv[0], lobjId);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_unlink,
&retval, -1, &result_len, 1, argv, 1);
--
2.50.1 (Apple Git-155)
Attachments:
[text/plain] v4-0001-remove-PQfn.patch (15.4K, ../aiGSa7XpUTRFt-be@nathan/2-v4-0001-remove-PQfn.patch)
download | inline diff:
From 63486371b3531be66bb8e0c62ef1b8cc7e95abb5 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Mon, 1 Jun 2026 16:55:00 -0500
Subject: [PATCH v4 1/3] remove PQfn
---
.../appendix-obsolete-libpq-fastpath.sgml | 24 ++++
doc/src/sgml/appendix-obsolete.sgml | 1 +
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/libpq.sgml | 117 +-----------------
src/backend/tcop/fastpath.c | 4 +-
src/include/tcop/dest.h | 4 +-
src/interfaces/libpq/fe-exec.c | 42 +++++--
src/interfaces/libpq/fe-lobj.c | 38 +++---
8 files changed, 86 insertions(+), 145 deletions(-)
create mode 100644 doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml
diff --git a/doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml b/doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml
new file mode 100644
index 00000000000..e42c2ece487
--- /dev/null
+++ b/doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml
@@ -0,0 +1,24 @@
+<!-- doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml -->
+<!--
+ See doc/src/sgml/appendix-obsolete.sgml for why this file exists. Do not change the id attribute.
+-->
+
+<sect1 id="libpq-fastpath">
+ <title><application>libpq</application> Fast-Path Interface Removed</title>
+
+ <indexterm zone="libpq-fastpath">
+ <primary>fast path</primary>
+ </indexterm>
+
+ <para>
+ In <productname>PostgreSQL</productname> 19 and below,
+ <application>libpq</application> supported a fast-path interface to send
+ simple function calls to the server. This interface was unsafe and
+ obsolete, and thus was removed in <productname>PostgreSQL</productname> 20.
+ 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>
+
+</sect1>
diff --git a/doc/src/sgml/appendix-obsolete.sgml b/doc/src/sgml/appendix-obsolete.sgml
index cc002653052..11a033112ef 100644
--- a/doc/src/sgml/appendix-obsolete.sgml
+++ b/doc/src/sgml/appendix-obsolete.sgml
@@ -39,5 +39,6 @@
&obsolete-pgresetxlog;
&obsolete-pgreceivexlog;
&obsolete-auth-radius;
+ &obsolete-libpq-fastpath;
</appendix>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 25a85082759..7dbe741d729 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -209,3 +209,4 @@
<!ENTITY obsolete-pgresetxlog SYSTEM "appendix-obsolete-pgresetxlog.sgml">
<!ENTITY obsolete-pgreceivexlog SYSTEM "appendix-obsolete-pgreceivexlog.sgml">
<!ENTITY obsolete-auth-radius SYSTEM "appendix-obsolete-auth-radius.sgml">
+<!ENTITY obsolete-libpq-fastpath SYSTEM "appendix-obsolete-libpq-fastpath.sgml">
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 7d3c3bb66d8..123e7f03902 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -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..5379e4ad9f5 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -11,7 +11,9 @@
* src/backend/tcop/fastpath.c
*
* NOTES
- * This cruft is the server side of PQfn.
+ * This cruft is the server side of PQfn. libpq's PQfn() was retired in
+ * v20 and now always errors, but the server code is retained for the
+ * benefit of 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..2f034d70e65 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -2986,10 +2986,10 @@ 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 (unless the connection is in the wrong state, in
+ * which case it returns NULL).
* ----------------
*/
@@ -3002,15 +3002,43 @@ PQfn(PGconn *conn,
const PQArgBlock *args,
int nargs)
{
- return PQnfn(conn, fnid, result_buf, -1, result_len,
- result_is_int, args, 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;
+ }
+
+ libpq_append_conn_error(conn, "PQfn() is no longer supported; use a prepared statement or PQexecParams() with binary results instead");
+ pqSaveErrorResult(conn);
+ return pqPrepareAsyncResult(conn);
}
/*
* 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.
+ * this verification. This is currently only used by the frontend LO
+ * interface and will hopefully be removed down the road.
*/
PGresult *
PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len,
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 12a32fcbaf3..1660c969f58 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -72,7 +72,7 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
argv[1].len = 4;
argv[1].u.integer = mode;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_open, &fd, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -106,8 +106,8 @@ lo_close(PGconn *conn, int fd)
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);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_close,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -169,8 +169,8 @@ lo_truncate(PGconn *conn, int fd, size_t len)
argv[1].len = 4;
argv[1].u.integer = (int) len;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate,
- &retval, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate,
+ &retval, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
@@ -218,8 +218,8 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
argv[1].len = 8;
argv[1].u.ptr = (int *) &len;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64,
- &retval, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate64,
+ &retval, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
@@ -322,8 +322,8 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len)
argv[1].len = (int) len;
argv[1].u.ptr = (int *) unconstify(char *, buf);
- res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
- &retval, &result_len, 1, argv, 2);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_write,
+ &retval, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -363,8 +363,8 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
argv[2].len = 4;
argv[2].u.integer = whence;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek,
- &retval, &result_len, 1, argv, 3);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek,
+ &retval, -1, &result_len, 1, argv, 3);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -448,8 +448,8 @@ lo_creat(PGconn *conn, int mode)
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);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_creat,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -492,8 +492,8 @@ lo_create(PGconn *conn, Oid lobjId)
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);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_create,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -526,8 +526,8 @@ lo_tell(PGconn *conn, int fd)
argv[0].len = 4;
argv[0].u.integer = fd;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_tell,
- &retval, &result_len, 1, argv, 1);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
@@ -600,8 +600,8 @@ lo_unlink(PGconn *conn, Oid lobjId)
argv[0].len = 4;
argv[0].u.integer = lobjId;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
- &retval, &result_len, 1, argv, 1);
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_unlink,
+ &retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
{
PQclear(res);
--
2.50.1 (Apple Git-155)
[text/plain] v4-0002-remove-lo_hton64-and-lo_ntoh64.patch (2.6K, ../aiGSa7XpUTRFt-be@nathan/3-v4-0002-remove-lo_hton64-and-lo_ntoh64.patch)
download | inline diff:
From 181028cbf820700f104f48dfc0e94d0c7f5e42e0 Mon Sep 17 00:00:00 2001
From: Reviewer <review@example.com>
Date: Tue, 2 Jun 2026 17:22:48 +0000
Subject: [PATCH v4 2/3] remove lo_hton64() and lo_ntoh64()
---
src/interfaces/libpq/fe-lobj.c | 58 +++-------------------------------
1 file changed, 4 insertions(+), 54 deletions(-)
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 1660c969f58..42b2a36bda9 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -43,8 +43,6 @@
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);
/*
* lo_open
@@ -213,7 +211,7 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
argv[0].len = 4;
argv[0].u.integer = fd;
- len = lo_hton64(len);
+ len = pg_hton64(len);
argv[1].isint = 0;
argv[1].len = 8;
argv[1].u.ptr = (int *) &len;
@@ -403,7 +401,7 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
argv[0].len = 4;
argv[0].u.integer = fd;
- offset = lo_hton64(offset);
+ offset = pg_hton64(offset);
argv[1].isint = 0;
argv[1].len = 8;
argv[1].u.ptr = (int *) &offset;
@@ -417,7 +415,7 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
{
PQclear(res);
- return lo_ntoh64(retval);
+ return pg_ntoh64(retval);
}
else
{
@@ -571,7 +569,7 @@ lo_tell64(PGconn *conn, int fd)
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
{
PQclear(res);
- return lo_ntoh64(retval);
+ return pg_ntoh64(retval);
}
else
{
@@ -1014,51 +1012,3 @@ lo_initialize(PGconn *conn)
conn->lobjfuncs = lobjfuncs;
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;
-}
--
2.50.1 (Apple Git-155)
[text/plain] v4-0003-add-helper-functions-for-fast-path-arg-setup.patch (6.0K, ../aiGSa7XpUTRFt-be@nathan/4-v4-0003-add-helper-functions-for-fast-path-arg-setup.patch)
download | inline diff:
From 8e5a937d5e302a56ff2118ce3ea4f69e7db5ae9f Mon Sep 17 00:00:00 2001
From: Reviewer <review@example.com>
Date: Tue, 2 Jun 2026 18:24:30 +0000
Subject: [PATCH v4 3/3] add helper functions for fast-path arg setup
---
src/interfaces/libpq/fe-lobj.c | 110 ++++++++++++---------------------
1 file changed, 38 insertions(+), 72 deletions(-)
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
index 42b2a36bda9..e8f6d958e84 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -44,6 +44,22 @@
static int lo_initialize(PGconn *conn);
static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid);
+static inline void
+lo_set_int_arg(PQArgBlock *arg, int value)
+{
+ arg->isint = 1;
+ arg->len = 4;
+ arg->u.integer = value;
+}
+
+static inline void
+lo_set_ptr_arg(PQArgBlock *arg, const void *ptr, int len)
+{
+ arg->isint = 0;
+ arg->len = len;
+ arg->u.ptr = (int *) unconstify(void *, ptr);
+}
+
/*
* lo_open
* opens an existing large object
@@ -62,13 +78,8 @@ lo_open(PGconn *conn, Oid lobjId, int mode)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = mode;
+ lo_set_int_arg(&argv[0], lobjId);
+ lo_set_int_arg(&argv[1], mode);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_open, &fd, -1, &result_len, 1, argv, 2);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -101,9 +112,7 @@ lo_close(PGconn *conn, int fd)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_close,
&retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -159,13 +168,8 @@ lo_truncate(PGconn *conn, int fd, size_t len)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = (int) len;
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_int_arg(&argv[1], (int) len);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate,
&retval, -1, &result_len, 1, argv, 2);
@@ -207,14 +211,10 @@ lo_truncate64(PGconn *conn, int fd, int64_t len)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
len = pg_hton64(len);
- argv[1].isint = 0;
- argv[1].len = 8;
- argv[1].u.ptr = (int *) &len;
+ lo_set_ptr_arg(&argv[1], &len, 8);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_truncate64,
&retval, -1, &result_len, 1, argv, 2);
@@ -261,13 +261,8 @@ 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;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = (int) len;
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_int_arg(&argv[1], (int) len);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_read,
(void *) buf, len, &result_len, 0, argv, 2);
@@ -312,13 +307,8 @@ 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;
-
- argv[1].isint = 0;
- argv[1].len = (int) len;
- argv[1].u.ptr = (int *) unconstify(char *, buf);
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_ptr_arg(&argv[1], buf, (int) len);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_write,
&retval, -1, &result_len, 1, argv, 2);
@@ -349,17 +339,9 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = offset;
-
- argv[2].isint = 1;
- argv[2].len = 4;
- argv[2].u.integer = whence;
+ lo_set_int_arg(&argv[0], fd);
+ lo_set_int_arg(&argv[1], offset);
+ lo_set_int_arg(&argv[2], whence);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek,
&retval, -1, &result_len, 1, argv, 3);
@@ -397,18 +379,12 @@ lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
offset = pg_hton64(offset);
- argv[1].isint = 0;
- argv[1].len = 8;
- argv[1].u.ptr = (int *) &offset;
+ lo_set_ptr_arg(&argv[1], &offset, 8);
- argv[2].isint = 1;
- argv[2].len = 4;
- argv[2].u.integer = whence;
+ lo_set_int_arg(&argv[2], whence);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek64,
(void *) &retval, sizeof(retval), &result_len, 0, argv, 3);
@@ -443,9 +419,7 @@ lo_creat(PGconn *conn, int mode)
if (lo_initialize(conn) < 0)
return InvalidOid;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = mode;
+ lo_set_int_arg(&argv[0], mode);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_creat,
&retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -487,9 +461,7 @@ lo_create(PGconn *conn, Oid lobjId)
return InvalidOid;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
+ lo_set_int_arg(&argv[0], lobjId);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_create,
&retval, -1, &result_len, 1, argv, 1);
if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -520,9 +492,7 @@ lo_tell(PGconn *conn, int fd)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell,
&retval, -1, &result_len, 1, argv, 1);
@@ -560,9 +530,7 @@ lo_tell64(PGconn *conn, int fd)
return -1;
}
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
+ lo_set_int_arg(&argv[0], fd);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell64,
(void *) &retval, sizeof(retval), &result_len, 0, argv, 1);
@@ -594,9 +562,7 @@ lo_unlink(PGconn *conn, Oid lobjId)
if (lo_initialize(conn) < 0)
return -1;
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
+ lo_set_int_arg(&argv[0], lobjId);
res = PQnfn(conn, conn->lobjfuncs->fn_lo_unlink,
&retval, -1, &result_len, 1, argv, 1);
--
2.50.1 (Apple Git-155)
view thread (20+ messages) latest in thread
Message-ID: <aiGSa7XpUTRFt-be@nathan>
Permalink: ../aiGSa7XpUTRFt-be@nathan/
Also on: postgresql.org/message-id/aiGSa7XpUTRFt-be@nathan
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: nathandbossart@gmail.com, ilmari@ilmari.org, jacob.champion@enterprisedb.com
Subject: Re: future of PQfn()
In-Reply-To: <aiGSa7XpUTRFt-be@nathan>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox