Thread

  1. [PATCH v1 2/3] remove support for PQfn

    Nathan Bossart <nathan@postgresql.org> — 2026-05-22T19:00:50Z

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