<varlistentry id="libpq-PQconnectStartParams">
<term><function>PQconnectStartParams</function><indexterm><primary>PQconnectStartParams</primary></indexterm></term>
<term><function>PQconnectStart</function><indexterm><primary>PQconnectStart</primary></indexterm></term>
- <term><function>PQconnectPoll</function><indexterm><primary>PQconnectPoll</primary></indexterm></term>
+ <term id="libpq-PQconnectPoll"><function>PQconnectPoll</function><indexterm><primary>PQconnectPoll</primary></indexterm></term>
<listitem>
<para>
<indexterm><primary>nonblocking connection</primary></indexterm>
</varlistentry>
<varlistentry id="libpq-PQerrorMessage">
- <term><function>PQerrorMessage</function><indexterm><primary>PQerrorMessage</primary></indexterm></term>
+ <term>
+ <function>PQerrorMessage</function><indexterm><primary>PQerrorMessage</primary></indexterm>
+ <indexterm><primary>error message</primary><secondary>in <structname>PGconn</structname></secondary></indexterm>
+ </term>
<listitem>
<para>
- <indexterm><primary>error message</primary></indexterm> Returns the error message
- most recently generated by an operation on the connection.
+ Returns the error message most recently generated by
+ an operation on the connection.
<synopsis>
char *PQerrorMessage(const PGconn *conn);
</synopsis>
-
</para>
<para>
<xref linkend="libpq-PQsendQuery"/>/<xref linkend="libpq-PQgetResult"/>
can also attempt to cancel a command that is still being processed
by the server; see <xref linkend="libpq-cancel"/>. But regardless of
- the return value of <xref linkend="libpq-PQcancel"/>, the application
+ the return value of <xref linkend="libpq-PQcancelBlocking"/>, the application
must continue with the normal result-reading sequence using
<xref linkend="libpq-PQgetResult"/>. A successful cancellation will
simply cause the command to terminate sooner than it would have
<title>Canceling Queries in Progress</title>
<indexterm zone="libpq-cancel">
- <primary>canceling</primary>
- <secondary>SQL command</secondary>
+ <primary>canceling SQL queries</primary>
+ </indexterm>
+ <indexterm zone="libpq-cancel">
+ <primary>query cancellation</primary>
</indexterm>
- <para>
- A client application can request cancellation of a command that is
- still being processed by the server, using the functions described in
- this section.
+ <sect2 id="libpq-cancel-functions">
+ <title>Functions for Sending Cancel Requests</title>
+ <variablelist>
+ <varlistentry id="libpq-PQcancelCreate">
+ <term><function>PQcancelCreate</function><indexterm><primary>PQcancelCreate</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Prepares a connection over which a cancel request can be sent.
+<synopsis>
+PGcancelConn *PQcancelCreate(PGconn *conn);
+</synopsis>
+ </para>
+
+ <para>
+ <xref linkend="libpq-PQcancelCreate"/> creates a
+ <structname>PGcancelConn</structname><indexterm><primary>PGcancelConn</primary></indexterm>
+ object, but it won't instantly start sending a cancel request over this
+ connection. A cancel request can be sent over this connection in a
+ blocking manner using <xref linkend="libpq-PQcancelBlocking"/> and in a
+ non-blocking manner using <xref linkend="libpq-PQcancelStart"/>.
+ The return value can be passed to <xref linkend="libpq-PQcancelStatus"/>
+ to check if the <structname>PGcancelConn</structname> object was
+ created successfully. The <structname>PGcancelConn</structname> object
+ is an opaque structure that is not meant to be accessed directly by the
+ application. This <structname>PGcancelConn</structname> object can be
+ used to cancel the query that's running on the original connection in a
+ thread-safe way.
+ </para>
+
+ <para>
+ Many connection parameters of the original client will be reused when
+ setting up the connection for the cancel request. Importantly, if the
+ original connection requires encryption of the connection and/or
+ verification of the target host (using <literal>sslmode</literal> or
+ <literal>gssencmode</literal>), then the connection for the cancel
+ request is made with these same requirements. Any connection options
+ that are only used during authentication or after authentication of the
+ client are ignored though, because cancellation requests do not require
+ authentication and the connection is closed right after the cancellation
+ request is submitted.
+ </para>
+
+ <para>
+ Note that when <function>PQcancelCreate</function> returns a non-null
+ pointer, you must call <xref linkend="libpq-PQcancelFinish"/> when you
+ are finished with it, in order to dispose of the structure and any
+ associated memory blocks. This must be done even if the cancel request
+ failed or was abandoned.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQcancelBlocking">
+ <term><function>PQcancelBlocking</function><indexterm><primary>PQcancelBlocking</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Requests that the server abandons processing of the current command
+ in a blocking manner.
+<synopsis>
+int PQcancelBlocking(PGcancelConn *cancelConn);
+</synopsis>
+ </para>
+
+ <para>
+ The request is made over the given <structname>PGcancelConn</structname>,
+ which needs to be created with <xref linkend="libpq-PQcancelCreate"/>.
+ The return value of <xref linkend="libpq-PQcancelBlocking"/>
+ is 1 if the cancel request was successfully
+ dispatched and 0 if not. If it was unsuccessful, the error message can be
+ retrieved using <xref linkend="libpq-PQcancelErrorMessage"/>.
+ </para>
+
+ <para>
+ Successful dispatch of the cancellation is no guarantee that the request
+ will have any effect, however. If the cancellation is effective, the
+ command being canceled will terminate early and return an error result.
+ If the cancellation fails (say, because the server was already done
+ processing the command), then there will be no visible result at all.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQcancelStart">
+ <term><function>PQcancelStart</function><indexterm><primary>PQcancelStart</primary></indexterm></term>
+ <term><function>PQcancelPoll</function><indexterm><primary>PQcancelPoll</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Requests that the server abandons processing of the current command
+ in a non-blocking manner.
+<synopsis>
+int PQcancelStart(PGcancelConn *cancelConn);
+
+PostgresPollingStatusType PQcancelPoll(PGcancelConn *cancelConn);
+</synopsis>
+ </para>
+
+ <para>
+ The request is made over the given <structname>PGcancelConn</structname>,
+ which needs to be created with <xref linkend="libpq-PQcancelCreate"/>.
+ The return value of <xref linkend="libpq-PQcancelStart"/>
+ is 1 if the cancellation request could be started and 0 if not.
+ If it was unsuccessful, the error message can be
+ retrieved using <xref linkend="libpq-PQcancelErrorMessage"/>.
+ </para>
+
+ <para>
+ If <function>PQcancelStart</function> succeeds, the next stage
+ is to poll <application>libpq</application> so that it can proceed with
+ the cancel connection sequence.
+ Use <xref linkend="libpq-PQcancelSocket"/> to obtain the descriptor of the
+ socket underlying the database connection.
+ (Caution: do not assume that the socket remains the same
+ across <function>PQcancelPoll</function> calls.)
+ Loop thus: If <function>PQcancelPoll(cancelConn)</function> last returned
+ <symbol>PGRES_POLLING_READING</symbol>, wait until the socket is ready to
+ read (as indicated by <function>select()</function>,
+ <function>poll()</function>, or similar system function).
+ Then call <function>PQcancelPoll(cancelConn)</function> again.
+ Conversely, if <function>PQcancelPoll(cancelConn)</function> last returned
+ <symbol>PGRES_POLLING_WRITING</symbol>, wait until the socket is ready
+ to write, then call <function>PQcancelPoll(cancelConn)</function> again.
+ On the first iteration, i.e., if you have yet to call
+ <function>PQcancelPoll(cancelConn)</function>, behave as if it last returned
+ <symbol>PGRES_POLLING_WRITING</symbol>. Continue this loop until
+ <function>PQcancelPoll(cancelConn)</function> returns
+ <symbol>PGRES_POLLING_FAILED</symbol>, indicating the connection procedure
+ has failed, or <symbol>PGRES_POLLING_OK</symbol>, indicating cancel
+ request was successfully dispatched.
+ </para>
+
+ <para>
+ Successful dispatch of the cancellation is no guarantee that the request
+ will have any effect, however. If the cancellation is effective, the
+ command being canceled will terminate early and return an error result.
+ If the cancellation fails (say, because the server was already done
+ processing the command), then there will be no visible result at all.
+ </para>
+
+ <para>
+ At any time during connection, the status of the connection can be
+ checked by calling <xref linkend="libpq-PQcancelStatus"/>.
+ If this call returns <symbol>CONNECTION_BAD</symbol>, then
+ the cancel procedure has failed; if the call returns
+ <function>CONNECTION_OK</function>, then cancel request was
+ successfully dispatched.
+ Both of these states are equally detectable from the return value of
+ <function>PQcancelPoll</function>, described above.
+ Other states might also occur during (and only during) an asynchronous
+ connection procedure.
+ These indicate the current stage of the connection procedure and might
+ be useful to provide feedback to the user for example.
+ These statuses are:
+
+ <variablelist>
+ <varlistentry id="libpq-cancel-connection-allocated">
+ <term><symbol>CONNECTION_ALLOCATED</symbol></term>
+ <listitem>
+ <para>
+ Waiting for a call to <xref linkend="libpq-PQcancelStart"/> or
+ <xref linkend="libpq-PQcancelBlocking"/>, to actually open the
+ socket. This is the connection state right after
+ calling <xref linkend="libpq-PQcancelCreate"/>
+ or <xref linkend="libpq-PQcancelReset"/>. No connection to the
+ server has been initiated yet at this point. To actually start
+ sending the cancel request use <xref linkend="libpq-PQcancelStart"/> or
+ <xref linkend="libpq-PQcancelBlocking"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-cancel-connection-started">
+ <term><symbol>CONNECTION_STARTED</symbol></term>
+ <listitem>
+ <para>
+ Waiting for connection to be made.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-cancel-connection-made">
+ <term><symbol>CONNECTION_MADE</symbol></term>
+ <listitem>
+ <para>
+ Connection OK; waiting to send.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-cancel-connection-awaiting-response">
+ <term><symbol>CONNECTION_AWAITING_RESPONSE</symbol></term>
+ <listitem>
+ <para>
+ Waiting for a response from the server.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-cancel-connection-ssl-startup">
+ <term><symbol>CONNECTION_SSL_STARTUP</symbol></term>
+ <listitem>
+ <para>
+ Negotiating SSL encryption.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-cancel-connection-gss-startup">
+ <term><symbol>CONNECTION_GSS_STARTUP</symbol></term>
+ <listitem>
+ <para>
+ Negotiating GSS encryption.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Note that, although these constants will remain (in order to maintain
+ compatibility), an application should never rely upon these occurring in a
+ particular order, or at all, or on the status always being one of these
+ documented values. An application might do something like this:
+<programlisting>
+switch(PQcancelStatus(conn))
+{
+ case CONNECTION_STARTED:
+ feedback = "Connecting...";
+ break;
+
+ case CONNECTION_MADE:
+ feedback = "Connected to server...";
+ break;
+.
+.
+.
+ default:
+ feedback = "Connecting...";
+}
+</programlisting>
+ </para>
+
+ <para>
+ The <literal>connect_timeout</literal> connection parameter is ignored
+ when using <function>PQcancelPoll</function>; it is the application's
+ responsibility to decide whether an excessive amount of time has elapsed.
+ Otherwise, <function>PQcancelStart</function> followed by a
+ <function>PQcancelPoll</function> loop is equivalent to
+ <xref linkend="libpq-PQcancelBlocking"/>.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQcancelStatus">
+ <term><function>PQcancelStatus</function><indexterm><primary>PQcancelStatus</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Returns the status of the cancel connection.
+<synopsis>
+ConnStatusType PQcancelStatus(const PGcancelConn *cancelConn);
+</synopsis>
+ </para>
+
+ <para>
+ The status can be one of a number of values. However, only three of
+ these are seen outside of an asynchronous cancel procedure:
+ <literal>CONNECTION_ALLOCATED</literal>,
+ <literal>CONNECTION_OK</literal> and
+ <literal>CONNECTION_BAD</literal>. The initial state of a
+ <function>PGcancelConn</function> that's successfully created using
+ <xref linkend="libpq-PQcancelCreate"/> is <literal>CONNECTION_ALLOCATED</literal>.
+ A cancel request that was successfully dispatched
+ has the status <literal>CONNECTION_OK</literal>. A failed
+ cancel attempt is signaled by status
+ <literal>CONNECTION_BAD</literal>. An OK status will
+ remain so until <xref linkend="libpq-PQcancelFinish"/> or
+ <xref linkend="libpq-PQcancelReset"/> is called.
+ </para>
+
+ <para>
+ See the entry for <xref linkend="libpq-PQcancelStart"/> with regards
+ to other status codes that might be returned.
+ </para>
+
+ <para>
+ Successful dispatch of the cancellation is no guarantee that the request
+ will have any effect, however. If the cancellation is effective, the
+ command being canceled will terminate early and return an error result.
+ If the cancellation fails (say, because the server was already done
+ processing the command), then there will be no visible result at all.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQcancelSocket">
+ <term><function>PQcancelSocket</function><indexterm><primary>PQcancelSocket</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Obtains the file descriptor number of the cancel connection socket to
+ the server.
+<synopsis>
+int PQcancelSocket(const PGcancelConn *cancelConn);
+</synopsis>
+ </para>
+
+ <para>
+ A valid descriptor will be greater than or equal to 0;
+ a result of -1 indicates that no server connection is currently open.
+ This might change as a result of calling any of the functions
+ in this section on the <structname>PQcancelConn</structname>
+ (except for <xref linkend="libpq-PQcancelErrorMessage"/> and
+ <function>PQcancelSocket</function> itself).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQcancelErrorMessage">
+ <term>
+ <function>PQcancelErrorMessage</function><indexterm><primary>PQcancelErrorMessage</primary></indexterm>
+ <indexterm><primary>error message</primary><secondary>in <structname>PGcancelConn</structname></secondary></indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ Returns the error message most recently generated by an
+ operation on the cancel connection.
+<synopsis>
+char *PQcancelErrorMessage(const PGcancelConn *cancelconn);
+</synopsis>
+ </para>
+
+ <para>
+ Nearly all <application>libpq</application> functions that take a
+ <structname>PGcancelConn</structname> will set a message for
+ <xref linkend="libpq-PQcancelErrorMessage"/> if they fail.
+ Note that by <application>libpq</application> convention,
+ a nonempty <xref linkend="libpq-PQcancelErrorMessage"/> result
+ can consist of multiple lines, and will include a trailing newline.
+ The caller should not free the result directly.
+ It will be freed when the associated
+ <structname>PGcancelConn</structname> handle is passed to
+ <xref linkend="libpq-PQcancelFinish"/>. The result string should not be
+ expected to remain the same across operations on the
+ <literal>PGcancelConn</literal> structure.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQcancelFinish">
+ <term><function>PQcancelFinish</function><indexterm><primary>PQcancelFinish</primary></indexterm></term>
+ <listitem>
+ <para>
+ Closes the cancel connection (if it did not finish sending the
+ cancel request yet). Also frees memory used by the
+ <structname>PGcancelConn</structname> object.
+<synopsis>
+void PQcancelFinish(PGcancelConn *cancelConn);
+</synopsis>
+ </para>
+
+ <para>
+ Note that even if the cancel attempt fails (as
+ indicated by <xref linkend="libpq-PQcancelStatus"/>), the
+ application should call <xref linkend="libpq-PQcancelFinish"/>
+ to free the memory used by the <structname>PGcancelConn</structname>
+ object.
+ The <structname>PGcancelConn</structname> pointer must not be used
+ again after <xref linkend="libpq-PQcancelFinish"/> has been called.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQcancelReset">
+ <term><function>PQcancelReset</function><indexterm><primary>PQcancelReset</primary></indexterm></term>
+ <listitem>
+ <para>
+ Resets the <symbol>PGcancelConn</symbol> so it can be reused for a new
+ cancel connection.
+<synopsis>
+void PQcancelReset(PGcancelConn *cancelConn);
+</synopsis>
+ </para>
+
+ <para>
+ If the <symbol>PGcancelConn</symbol> is currently used to send a cancel
+ request, then this connection is closed. It will then prepare the
+ <symbol>PGcancelConn</symbol> object such that it can be used to send a
+ new cancel request.
+ </para>
+
+ <para>
+ This can be used to create one <structname>PGcancelConn</structname>
+ for a <structname>PGconn</structname> and reuse it multiple times
+ throughout the lifetime of the original <structname>PGconn</structname>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+
+ <sect2 id="libpq-cancel-deprecated">
+ <title>Obsolete Functions for Sending Cancel Requests</title>
+
+ <para>
+ These functions represent older methods of sending cancel requests.
+ Although they still work, they are deprecated due to not sending the cancel
+ requests in an encrypted manner, even when the original connection
+ specified <literal>sslmode</literal> or <literal>gssencmode</literal> to
+ require encryption. Thus these older methods are heavily discouraged from
+ being used in new code, and it is recommended to change existing code to
+ use the new functions instead.
+ </para>
<variablelist>
<varlistentry id="libpq-PQgetCancel">
<listitem>
<para>
Creates a data structure containing the information needed to cancel
- a command issued through a particular database connection.
+ a command using <xref linkend="libpq-PQcancel"/>.
<synopsis>
PGcancel *PQgetCancel(PGconn *conn);
</synopsis>
<para>
<xref linkend="libpq-PQgetCancel"/> creates a
- <structname>PGcancel</structname><indexterm><primary>PGcancel</primary></indexterm> object
- given a <structname>PGconn</structname> connection object. It will return
- <symbol>NULL</symbol> if the given <parameter>conn</parameter> is <symbol>NULL</symbol> or an invalid
- connection. The <structname>PGcancel</structname> object is an opaque
+ <structname>PGcancel</structname><indexterm><primary>PGcancel</primary></indexterm>
+ object given a <structname>PGconn</structname> connection object.
+ It will return <symbol>NULL</symbol> if the given <parameter>conn</parameter>
+ is <symbol>NULL</symbol> or an invalid connection.
+ The <structname>PGcancel</structname> object is an opaque
structure that is not meant to be accessed directly by the
application; it can only be passed to <xref linkend="libpq-PQcancel"/>
or <xref linkend="libpq-PQfreeCancel"/>.
<listitem>
<para>
- Requests that the server abandon processing of the current command.
+ <xref linkend="libpq-PQcancel"/> is a deprecated and insecure
+ variant of <xref linkend="libpq-PQcancelBlocking"/>, but one that can be
+ used safely from within a signal handler.
<synopsis>
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize);
</synopsis>
</para>
<para>
- The return value is 1 if the cancel request was successfully
- dispatched and 0 if not. If not, <parameter>errbuf</parameter> is filled
- with an explanatory error message. <parameter>errbuf</parameter>
- must be a char array of size <parameter>errbufsize</parameter> (the
- recommended size is 256 bytes).
+ <xref linkend="libpq-PQcancel"/> only exists because of backwards
+ compatibility reasons. <xref linkend="libpq-PQcancelBlocking"/> should be
+ used instead. The only benefit that <xref linkend="libpq-PQcancel"/> has
+ is that it can be safely invoked from a signal handler, if the
+ <parameter>errbuf</parameter> is a local variable in the signal handler.
+ However, this is generally not considered a big enough benefit to be
+ worth the security issues that this function has.
</para>
<para>
- Successful dispatch is no guarantee that the request will have
- any effect, however. If the cancellation is effective, the current
- command will terminate early and return an error result. If the
- cancellation fails (say, because the server was already done
- processing the command), then there will be no visible result at
- all.
+ The <structname>PGcancel</structname> object is read-only as far as
+ <xref linkend="libpq-PQcancel"/> is concerned, so it can also be invoked
+ from a thread that is separate from the one manipulating the
+ <structname>PGconn</structname> object.
</para>
<para>
- <xref linkend="libpq-PQcancel"/> can safely be invoked from a signal
- handler, if the <parameter>errbuf</parameter> is a local variable in the
- signal handler. The <structname>PGcancel</structname> object is read-only
- as far as <xref linkend="libpq-PQcancel"/> is concerned, so it can
- also be invoked from a thread that is separate from the one
- manipulating the <structname>PGconn</structname> object.
+ The return value of <xref linkend="libpq-PQcancel"/> is 1 if the
+ cancel request was successfully dispatched and 0 if not.
+ If not, <parameter>errbuf</parameter> is filled with an explanatory
+ error message.
+ <parameter>errbuf</parameter> must be a char array of size
+ <parameter>errbufsize</parameter> (the recommended size is 256 bytes).
</para>
</listitem>
</varlistentry>
<listitem>
<para>
- <xref linkend="libpq-PQrequestCancel"/> is a deprecated variant of
- <xref linkend="libpq-PQcancel"/>.
+ <xref linkend="libpq-PQrequestCancel"/> is a deprecated and insecure
+ variant of <xref linkend="libpq-PQcancelBlocking"/>.
<synopsis>
int PQrequestCancel(PGconn *conn);
</synopsis>
</para>
+ <para>
+ <xref linkend="libpq-PQrequestCancel"/> only exists because of backwards
+ compatibility reasons. <xref linkend="libpq-PQcancelBlocking"/> should be
+ used instead. There is no benefit to using
+ <xref linkend="libpq-PQrequestCancel"/> over
+ <xref linkend="libpq-PQcancelBlocking"/>.
+ </para>
+
<para>
Requests that the server abandon processing of the current
command. It operates directly on the
</listitem>
</varlistentry>
</variablelist>
- </para>
-
+ </sect2>
</sect1>
<sect1 id="libpq-fastpath">
The deprecated functions <xref linkend="libpq-PQrequestCancel"/> and
<xref linkend="libpq-PQoidStatus"/> are not thread-safe and should not be
used in multithread programs. <xref linkend="libpq-PQrequestCancel"/>
- can be replaced by <xref linkend="libpq-PQcancel"/>.
+ can be replaced by <xref linkend="libpq-PQcancelBlocking"/>.
<xref linkend="libpq-PQoidStatus"/> can be replaced by
<xref linkend="libpq-PQoidValue"/>.
</para>
#include "port/pg_bswap.h"
+/*
+ * pg_cancel_conn (backing struct for PGcancelConn) is a wrapper around a
+ * PGconn to send cancellations using PQcancelBlocking and PQcancelStart.
+ * This isn't just a typedef because we want the compiler to complain when a
+ * PGconn is passed to a function that expects a PGcancelConn, and vice versa.
+ */
+struct pg_cancel_conn
+{
+ PGconn conn;
+};
+
/*
* pg_cancel (backing struct for PGcancel) stores all data necessary to send a
* cancel request.
};
+/*
+ * PQcancelCreate
+ *
+ * Create and return a PGcancelConn, which can be used to securely cancel a
+ * query on the given connection.
+ *
+ * This requires either following the non-blocking flow through
+ * PQcancelStart() and PQcancelPoll(), or the blocking PQcancelBlocking().
+ */
+PGcancelConn *
+PQcancelCreate(PGconn *conn)
+{
+ PGconn *cancelConn = pqMakeEmptyPGconn();
+ pg_conn_host originalHost;
+
+ if (cancelConn == NULL)
+ return NULL;
+
+ /* Check we have an open connection */
+ if (!conn)
+ {
+ libpq_append_conn_error(cancelConn, "passed connection was NULL");
+ return (PGcancelConn *) cancelConn;
+ }
+
+ if (conn->sock == PGINVALID_SOCKET)
+ {
+ libpq_append_conn_error(cancelConn, "passed connection is not open");
+ return (PGcancelConn *) cancelConn;
+ }
+
+ /*
+ * Indicate that this connection is used to send a cancellation
+ */
+ cancelConn->cancelRequest = true;
+
+ if (!pqCopyPGconn(conn, cancelConn))
+ return (PGcancelConn *) cancelConn;
+
+ /*
+ * Compute derived options
+ */
+ if (!pqConnectOptions2(cancelConn))
+ return (PGcancelConn *) cancelConn;
+
+ /*
+ * Copy cancellation token data from the original connnection
+ */
+ cancelConn->be_pid = conn->be_pid;
+ cancelConn->be_key = conn->be_key;
+
+ /*
+ * Cancel requests should not iterate over all possible hosts. The request
+ * needs to be sent to the exact host and address that the original
+ * connection used. So we manually create the host and address arrays with
+ * a single element after freeing the host array that we generated from
+ * the connection options.
+ */
+ pqReleaseConnHosts(cancelConn);
+ cancelConn->nconnhost = 1;
+ cancelConn->naddr = 1;
+
+ cancelConn->connhost = calloc(cancelConn->nconnhost, sizeof(pg_conn_host));
+ if (!cancelConn->connhost)
+ goto oom_error;
+
+ originalHost = conn->connhost[conn->whichhost];
+ if (originalHost.host)
+ {
+ cancelConn->connhost[0].host = strdup(originalHost.host);
+ if (!cancelConn->connhost[0].host)
+ goto oom_error;
+ }
+ if (originalHost.hostaddr)
+ {
+ cancelConn->connhost[0].hostaddr = strdup(originalHost.hostaddr);
+ if (!cancelConn->connhost[0].hostaddr)
+ goto oom_error;
+ }
+ if (originalHost.port)
+ {
+ cancelConn->connhost[0].port = strdup(originalHost.port);
+ if (!cancelConn->connhost[0].port)
+ goto oom_error;
+ }
+ if (originalHost.password)
+ {
+ cancelConn->connhost[0].password = strdup(originalHost.password);
+ if (!cancelConn->connhost[0].password)
+ goto oom_error;
+ }
+
+ cancelConn->addr = calloc(cancelConn->naddr, sizeof(AddrInfo));
+ if (!cancelConn->connhost)
+ goto oom_error;
+
+ cancelConn->addr[0].addr = conn->raddr;
+ cancelConn->addr[0].family = conn->raddr.addr.ss_family;
+
+ cancelConn->status = CONNECTION_ALLOCATED;
+ return (PGcancelConn *) cancelConn;
+
+oom_error:
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(cancelConn, "out of memory");
+ return (PGcancelConn *) cancelConn;
+}
+
+
+/*
+ * PQcancelBlocking
+ *
+ * Send a cancellation request in a blocking fashion.
+ * Returns 1 if successful 0 if not.
+ */
+int
+PQcancelBlocking(PGcancelConn *cancelConn)
+{
+ if (!PQcancelStart(cancelConn))
+ return 0;
+ return pqConnectDBComplete(&cancelConn->conn);
+}
+
+/*
+ * PQcancelStart
+ *
+ * Starts sending a cancellation request in a non-blocking fashion. Returns
+ * 1 if successful 0 if not.
+ */
+int
+PQcancelStart(PGcancelConn *cancelConn)
+{
+ if (!cancelConn || cancelConn->conn.status == CONNECTION_BAD)
+ return 0;
+
+ if (cancelConn->conn.status != CONNECTION_ALLOCATED)
+ {
+ libpq_append_conn_error(&cancelConn->conn,
+ "cancel request is already being sent on this connection");
+ cancelConn->conn.status = CONNECTION_BAD;
+ return 0;
+ }
+
+ return pqConnectDBStart(&cancelConn->conn);
+}
+
+/*
+ * PQcancelPoll
+ *
+ * Poll a cancel connection. For usage details see PQconnectPoll.
+ */
+PostgresPollingStatusType
+PQcancelPoll(PGcancelConn *cancelConn)
+{
+ PGconn *conn = &cancelConn->conn;
+ int n;
+
+ /*
+ * We leave most of the connection establishement to PQconnectPoll, since
+ * it's very similar to normal connection establishment. But once we get
+ * to the CONNECTION_AWAITING_RESPONSE we need to start doing our own
+ * thing.
+ */
+ if (conn->status != CONNECTION_AWAITING_RESPONSE)
+ {
+ return PQconnectPoll(conn);
+ }
+
+ /*
+ * At this point we are waiting on the server to close the connection,
+ * which is its way of communicating that the cancel has been handled.
+ */
+
+ n = pqReadData(conn);
+
+ if (n == 0)
+ return PGRES_POLLING_READING;
+
+#ifndef WIN32
+
+ /*
+ * If we receive an error report it, but only if errno is non-zero.
+ * Otherwise we assume it's an EOF, which is what we expect from the
+ * server.
+ *
+ * We skip this for Windows, because Windows is a bit special in its EOF
+ * behaviour for TCP. Sometimes it will error with an ECONNRESET when
+ * there is a clean connection closure. See these threads for details:
+ * https://www.postgresql.org/message-id/flat/90b34057-4176-7bb0-0dbb-9822a5f6425b%40greiz-reinsdorf.de
+ *
+ * https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BOeoETZQ%3DQw5Ub5h3tmwQhBmDA%3DnuNO3KG%3DzWfUypFAw%40mail.gmail.com
+ *
+ * PQcancel ignores such errors and reports success for the cancellation
+ * anyway, so even if this is not always correct we do the same here.
+ */
+ if (n < 0 && errno != 0)
+ {
+ conn->status = CONNECTION_BAD;
+ return PGRES_POLLING_FAILED;
+ }
+#endif
+
+ /*
+ * We don't expect any data, only connection closure. So if we strangely
+ * do receive some data we consider that an error.
+ */
+ if (n > 0)
+ {
+ libpq_append_conn_error(conn, "received unexpected response from server");
+ conn->status = CONNECTION_BAD;
+ return PGRES_POLLING_FAILED;
+ }
+
+ /*
+ * Getting here means that we received an EOF, which is what we were
+ * expecting -- the cancel request has completed.
+ */
+ cancelConn->conn.status = CONNECTION_OK;
+ resetPQExpBuffer(&conn->errorMessage);
+ return PGRES_POLLING_OK;
+}
+
+/*
+ * PQcancelStatus
+ *
+ * Get the status of a cancel connection.
+ */
+ConnStatusType
+PQcancelStatus(const PGcancelConn *cancelConn)
+{
+ return PQstatus(&cancelConn->conn);
+}
+
+/*
+ * PQcancelSocket
+ *
+ * Get the socket of the cancel connection.
+ */
+int
+PQcancelSocket(const PGcancelConn *cancelConn)
+{
+ return PQsocket(&cancelConn->conn);
+}
+
+/*
+ * PQcancelErrorMessage
+ *
+ * Get the socket of the cancel connection.
+ */
+char *
+PQcancelErrorMessage(const PGcancelConn *cancelConn)
+{
+ return PQerrorMessage(&cancelConn->conn);
+}
+
+/*
+ * PQcancelReset
+ *
+ * Resets the cancel connection, so it can be reused to send a new cancel
+ * request.
+ */
+void
+PQcancelReset(PGcancelConn *cancelConn)
+{
+ pqClosePGconn(&cancelConn->conn);
+ cancelConn->conn.status = CONNECTION_ALLOCATED;
+ cancelConn->conn.whichhost = 0;
+ cancelConn->conn.whichaddr = 0;
+ cancelConn->conn.try_next_host = false;
+ cancelConn->conn.try_next_addr = false;
+}
+
+/*
+ * PQcancelFinish
+ *
+ * Closes and frees the cancel connection.
+ */
+void
+PQcancelFinish(PGcancelConn *cancelConn)
+{
+ PQfinish(&cancelConn->conn);
+}
+
/*
* PQgetCancel: get a PGcancel structure corresponding to a connection.
*
/*
- * PQcancel: request query cancel
+ * PQcancel: old, non-encrypted, but signal-safe way of requesting query cancel
*
* The return value is true if the cancel request was successfully
* dispatched, false if not (in which case an error message is available).
conn->write_failed = false;
free(conn->write_err_msg);
conn->write_err_msg = NULL;
- conn->be_pid = 0;
- conn->be_key = 0;
+
+ /*
+ * Cancel connections need to retain their be_pid and be_key across
+ * PQcancelReset invocations, otherwise they would not have access to the
+ * secret token of the connection they are supposed to cancel.
+ */
+ if (!conn->cancelRequest)
+ {
+ conn->be_pid = 0;
+ conn->be_key = 0;
+ }
}
return true;
}
+/*
+ * Copy over option values from srcConn to dstConn
+ *
+ * Don't put anything cute here --- intelligence should be in
+ * connectOptions2 ...
+ *
+ * Returns true on success. On failure, returns false and sets error message of
+ * dstConn.
+ */
+bool
+pqCopyPGconn(PGconn *srcConn, PGconn *dstConn)
+{
+ const internalPQconninfoOption *option;
+
+ /* copy over connection options */
+ for (option = PQconninfoOptions; option->keyword; option++)
+ {
+ if (option->connofs >= 0)
+ {
+ const char **tmp = (const char **) ((char *) srcConn + option->connofs);
+
+ if (*tmp)
+ {
+ char **dstConnmember = (char **) ((char *) dstConn + option->connofs);
+
+ if (*dstConnmember)
+ free(*dstConnmember);
+ *dstConnmember = strdup(*tmp);
+ if (*dstConnmember == NULL)
+ {
+ libpq_append_conn_error(dstConn, "out of memory");
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
/*
* connectOptions1
*
* Set up to try to connect to the first host. (Setting whichhost = -1 is
* a bit of a cheat, but PQconnectPoll will advance it to 0 before
* anything else looks at it.)
+ *
+ * Cancel requests are special though, they should only try one host and
+ * address, and these fields have already been set up in PQcancelCreate,
+ * so leave these fields alone for cancel requests.
*/
- conn->whichhost = -1;
- conn->try_next_addr = false;
- conn->try_next_host = true;
+ if (!conn->cancelRequest)
+ {
+ conn->whichhost = -1;
+ conn->try_next_host = true;
+ conn->try_next_addr = false;
+ }
+
conn->status = CONNECTION_NEEDED;
/* Also reset the target_server_type state if needed */
/*
* Now try to advance the state machine.
*/
- flag = PQconnectPoll(conn);
+ if (conn->cancelRequest)
+ flag = PQcancelPoll((PGcancelConn *) conn);
+ else
+ flag = PQconnectPoll(conn);
}
}
* Oops, no more hosts.
*
* If we are trying to connect in "prefer-standby" mode, then drop
- * the standby requirement and start over.
+ * the standby requirement and start over. Don't do this for
+ * cancel requests though, since we are certain the list of
+ * servers won't change as the target_server_type option is not
+ * applicable to those connections.
*
* Otherwise, an appropriate error message is already set up, so
* we just need to set the right status.
*/
if (conn->target_server_type == SERVER_TYPE_PREFER_STANDBY &&
- conn->nconnhost > 0)
+ conn->nconnhost > 0 &&
+ !conn->cancelRequest)
{
conn->target_server_type = SERVER_TYPE_PREFER_STANDBY_PASS2;
conn->whichhost = 0;
}
#endif /* USE_SSL */
+ /*
+ * For cancel requests this is as far as we need to go in the
+ * connection establishment. Now we can actually send our
+ * cancellation request.
+ */
+ if (conn->cancelRequest)
+ {
+ CancelRequestPacket cancelpacket;
+
+ packetlen = sizeof(cancelpacket);
+ cancelpacket.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
+ cancelpacket.backendPID = pg_hton32(conn->be_pid);
+ cancelpacket.cancelAuthCode = pg_hton32(conn->be_key);
+ if (pqPacketSend(conn, 0, &cancelpacket, packetlen) != STATUS_OK)
+ {
+ libpq_append_conn_error(conn, "could not send cancel packet: %s",
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ goto error_return;
+ }
+ conn->status = CONNECTION_AWAITING_RESPONSE;
+ return PGRES_POLLING_READING;
+ }
+
/*
* Build the startup packet.
*/
}
}
- /* We can release the address list now. */
- release_conn_addrinfo(conn);
+ /*
+ * For non cancel requests we can release the address list
+ * now. For cancel requests we never actually resolve
+ * addresses and instead the addrinfo exists for the lifetime
+ * of the connection.
+ */
+ if (!conn->cancelRequest)
+ release_conn_addrinfo(conn);
/*
* Contents of conn->errorMessage are no longer interesting
free(conn->events[i].name);
}
+ release_conn_addrinfo(conn);
pqReleaseConnHosts(conn);
free(conn->client_encoding_initial);
static void
sendTerminateConn(PGconn *conn)
{
+ /*
+ * The Postgres cancellation protocol does not have a notion of a
+ * Terminate message, so don't send one.
+ */
+ if (conn->cancelRequest)
+ return;
+
/*
* Note that the protocol doesn't allow us to send Terminate messages
* during the startup phase.
conn->pipelineStatus = PQ_PIPELINE_OFF;
pqClearAsyncResult(conn); /* deallocate result */
pqClearConnErrorState(conn);
- release_conn_addrinfo(conn);
+
+ /*
+ * Release addrinfo, but since cancel requests never change their addrinfo
+ * we don't do that. Otherwise we would have to rebuild it during a
+ * PQcancelReset.
+ */
+ if (!conn->cancelRequest)
+ release_conn_addrinfo(conn);
/* Reset all state obtained from server, too */
pqDropServerData(conn);