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..29da6505867
--- /dev/null
+++ b/doc/src/sgml/appendix-obsolete-libpq-fastpath.sgml
@@ -0,0 +1,30 @@
+
+
+
+
+ libpq Fast-Path Interface Removed
+
+
+ fast path
+
+
+
+ PQfn
+
+
+
+ In PostgreSQL 19 and below,
+ libpq supported a fast-path interface to send
+ simple function calls to the server via the PQfn
+ function. This interface was unsafe and obsolete, and thus was removed in
+ PostgreSQL 20. The PQfn
+ symbol still exists so that applications continue to link, but it now
+ always fails. 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.
+
+
+
diff --git a/doc/src/sgml/appendix-obsolete.sgml b/doc/src/sgml/appendix-obsolete.sgml
index 759a65cb5e0..23d9cc729ef 100644
--- a/doc/src/sgml/appendix-obsolete.sgml
+++ b/doc/src/sgml/appendix-obsolete.sgml
@@ -40,5 +40,6 @@
&obsolete-pgreceivexlog;
&obsolete-auth-radius;
&obsolete-refint;
+ &obsolete-libpq-fastpath;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 66ea8b988a1..0797dcf96da 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -210,3 +210,4 @@
+
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 COPY.
Using synchronous command execution functions
- such as PQfn,
+ such as
PQexec,
PQexecParams,
PQprepare,
@@ -7046,121 +7046,6 @@ int PQrequestCancel(PGconn *conn);
-
- The Fast-Path Interface
-
-
- fast path
-
-
-
- PostgreSQL provides a fast-path interface
- to send simple function calls to the server.
-
-
-
-
- This interface is unsafe and should not be used. When
- result_is_int is set to 0,
- PQfn may write data beyond the end of
- result_buf, 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.
-
-
-
-
- The function PQfnPQfn
- requests execution of a server function via the fast-path interface:
-
-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;
-
-
-
-
- The fnid argument is the OID of the function to be
- executed. args and nargs define the
- parameters to be passed to the function; they must match the declared
- function argument list. When the isint field of a
- parameter structure is true, the u.integer 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 isint
- is false, the indicated number of bytes at *u.ptr 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 u.ptr as being of
- type int * is historical; it would be better to consider
- it void *.)
- result_buf 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
- result_len. If a 2- or 4-byte integer result
- is expected, set result_is_int to 1, otherwise
- set it to 0. Setting result_is_int to 1 causes
- libpq to byte-swap the value if necessary, so that it
- is delivered as a proper int value for the client machine;
- note that a 4-byte integer is delivered into *result_buf
- for either allowed result size.
- When result_is_int is 0, the binary-format byte string
- sent by the server is returned unmodified. (In this case it's better
- to consider result_buf as being of
- type void *.)
-
-
-
- PQfn always returns a valid
- PGresult pointer, with
- status PGRES_COMMAND_OK for success
- or PGRES_FATAL_ERROR if some problem was encountered.
- The result status should be
- checked before the result is used. The caller is responsible for
- freeing the PGresult with
- when it is no longer needed.
-
-
-
- To pass a NULL argument to the function, set
- the len field of that parameter structure
- to -1; the isint
- and u fields are then irrelevant.
-
-
-
- If the function returns NULL, *result_len is set
- to -1, and *result_buf is not
- modified.
-
-
-
- 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.
-
-
-
-
Asynchronous Notification
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 52772bc90a8..365a45bcba9 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 and the frontend LO interface.
*
*-------------------------------------------------------------------------
*/
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..294690648bd 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. As before, NULL is returned instead when conn is
+ * NULL or when the connection state doesn't permit a query cycle.
* ----------------
*/
@@ -3002,15 +3002,52 @@ 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);
+
+ /*
+ * The following state checks may look pointless for a function that can
+ * no longer succeed, but they must stay ahead of the pqSaveErrorResult()
+ * call below, which discards any result that is still being assembled.
+ */
+ 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 "
+ "parameters and 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.
+ * Private version of the fast-path interface with verification that
+ * returned data fits in result_buf when result_is_int == 0. Setting
+ * buf_size to -1 disables 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 c914342c010..cad303f31fe 100644
--- a/src/interfaces/libpq/fe-lobj.c
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -70,7 +70,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);
@@ -104,8 +104,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);
@@ -167,8 +167,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)
{
@@ -216,8 +216,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)
{
@@ -320,8 +320,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);
@@ -361,8 +361,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);
@@ -446,8 +446,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);
@@ -490,8 +490,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);
@@ -524,8 +524,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);
@@ -598,8 +598,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);
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index f807670409c..45a2d49889a 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -2207,7 +2207,7 @@ pqEndcopy3(PGconn *conn)
/*
- * PQfn - Send a function call to the POSTGRES backend.
+ * PQnfn - Send a function call to the POSTGRES backend.
*
* See fe-exec.c for documentation.
*/
@@ -2224,10 +2224,10 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
int avail;
int i;
- /* already validated by PQfn */
+ /* already validated by PnQfn */
Assert(conn->pipelineStatus == PQ_PIPELINE_OFF);
- /* PQfn already validated connection state */
+ /* PQnfn already validated connection state */
if (pqPutMsgStart(PqMsg_FunctionCall, conn) < 0 ||
pqPutInt(fnid, 4, conn) < 0 || /* function id */
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 8ecb9b4a4c7..f51fd620b0a 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -584,8 +584,7 @@ extern PGPing PQpingParams(const char *const *keywords,
extern int PQflush(PGconn *conn);
/*
- * "Fast path" interface --- not really recommended for application
- * use
+ * "Fast path" interface --- no longer supported, always fails
*/
extern PGresult *PQfn(PGconn *conn,
int fnid,