summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/protocol.sgml5
-rw-r--r--src/interfaces/libpq/fe-cancel.c28
2 files changed, 32 insertions, 1 deletions
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index b115884acb3..e56eac8fd0f 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -537,6 +537,11 @@
The frontend should not respond to this message, but should
continue listening for a ReadyForQuery message.
</para>
+ <para>
+ The <productname>PostgreSQL</productname> server will always send this
+ message, but some third party backend implementations of the protocol
+ that don't support query cancellation are known not to.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/interfaces/libpq/fe-cancel.c b/src/interfaces/libpq/fe-cancel.c
index 65517c5703b..c872a0267f0 100644
--- a/src/interfaces/libpq/fe-cancel.c
+++ b/src/interfaces/libpq/fe-cancel.c
@@ -379,7 +379,24 @@ PQgetCancel(PGconn *conn)
/* Check that we have received a cancellation key */
if (conn->be_cancel_key_len == 0)
- return NULL;
+ {
+ /*
+ * In case there is no cancel key, return an all-zero PGcancel object.
+ * Actually calling PQcancel on this will fail, but we allow creating
+ * the PGcancel object anyway. Arguably it would be better return NULL
+ * to indicate that cancellation is not possible, but there'd be no
+ * way for the caller to distinguish "out of memory" from "server did
+ * not send a cancel key". Also, this is how PGgetCancel() has always
+ * behaved, and if we changed it, some clients would stop working
+ * altogether with servers that don't support cancellation. (The
+ * modern PQcancelCreate() function returns a failed connection object
+ * instead.)
+ *
+ * The returned dummy object has cancel_pkt_len == 0; we check for
+ * that in PQcancel() to identify it as a dummy.
+ */
+ return calloc(1, sizeof(PGcancel));
+ }
cancel_req_len = offsetof(CancelRequestPacket, cancelAuthCode) + conn->be_cancel_key_len;
cancel = malloc(offsetof(PGcancel, cancel_req) + cancel_req_len);
@@ -544,6 +561,15 @@ PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
return false;
}
+ if (cancel->cancel_pkt_len == 0)
+ {
+ /* This is a dummy PGcancel object, see PQgetCancel */
+ strlcpy(errbuf, "PQcancel() -- no cancellation key received", errbufsize);
+ /* strlcpy probably doesn't change errno, but be paranoid */
+ SOCK_ERRNO_SET(save_errno);
+ return false;
+ }
+
/*
* We need to open a temporary connection to the postmaster. Do this with
* only kernel calls.