Thread
-
[PATCH v2 3/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 | 246 ---------------------------- src/interfaces/libpq/libpq-int.h | 5 - 6 files changed, 12 insertions(+), 419 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 0407d10362d..f9c55ee7905 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -2235,252 +2235,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; - case PqMsg_PrepStmtDeallocated: - if (getPrepStmtDeallocated(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 7a32c14de5e..980ef5374b5 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -768,11 +768,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) --wfzLmV/Wl0ehOJBp--