agora inbox for [email protected]
help / color / mirror / Atom feedFrom: Nathan Bossart <[email protected]>
Subject: [PATCH v4 1/3] remove PQfn
Date: Mon, 1 Jun 2026 16:55:00 -0500
---
.../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)
--Z10p9YYojB0/BzD2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
filename=v4-0002-remove-lo_hton64-and-lo_ntoh64.patch
view thread (966+ messages) latest in thread
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: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v4 1/3] remove PQfn
In-Reply-To: <no-message-id-586631@localhost>
* 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