LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-cancel.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 11.0 % 290 32
Test Date: 2026-01-26 10:56:24 Functions: 13.3 % 15 2
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 6.8 % 146 10

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * fe-cancel.c
       4                 :             :  *        functions related to query cancellation
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *        src/interfaces/libpq/fe-cancel.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : 
      16                 :             : #include "postgres_fe.h"
      17                 :             : 
      18                 :             : #include <unistd.h>
      19                 :             : 
      20                 :             : #include "libpq-fe.h"
      21                 :             : #include "libpq-int.h"
      22                 :             : #include "port/pg_bswap.h"
      23                 :             : 
      24                 :             : 
      25                 :             : /*
      26                 :             :  * pg_cancel_conn (backing struct for PGcancelConn) is a wrapper around a
      27                 :             :  * PGconn to send cancellations using PQcancelBlocking and PQcancelStart.
      28                 :             :  * This isn't just a typedef because we want the compiler to complain when a
      29                 :             :  * PGconn is passed to a function that expects a PGcancelConn, and vice versa.
      30                 :             :  */
      31                 :             : struct pg_cancel_conn
      32                 :             : {
      33                 :             :         PGconn          conn;
      34                 :             : };
      35                 :             : 
      36                 :             : /*
      37                 :             :  * pg_cancel (backing struct for PGcancel) stores all data necessary to send a
      38                 :             :  * cancel request.
      39                 :             :  */
      40                 :             : struct pg_cancel
      41                 :             : {
      42                 :             :         SockAddr        raddr;                  /* Remote address */
      43                 :             :         int                     be_pid;                 /* PID of to-be-canceled backend */
      44                 :             :         int                     pgtcp_user_timeout; /* tcp user timeout */
      45                 :             :         int                     keepalives;             /* use TCP keepalives? */
      46                 :             :         int                     keepalives_idle;        /* time between TCP keepalives */
      47                 :             :         int                     keepalives_interval;    /* time between TCP keepalive
      48                 :             :                                                                                  * retransmits */
      49                 :             :         int                     keepalives_count;       /* maximum number of TCP keepalive
      50                 :             :                                                                          * retransmits */
      51                 :             : 
      52                 :             :         /* Pre-constructed cancel request packet starts here */
      53                 :             :         int32           cancel_pkt_len; /* in network byte order */
      54                 :             :         char            cancel_req[FLEXIBLE_ARRAY_MEMBER];      /* CancelRequestPacket */
      55                 :             : };
      56                 :             : 
      57                 :             : 
      58                 :             : /*
      59                 :             :  *              PQcancelCreate
      60                 :             :  *
      61                 :             :  * Create and return a PGcancelConn, which can be used to securely cancel a
      62                 :             :  * query on the given connection.
      63                 :             :  *
      64                 :             :  * This requires either following the non-blocking flow through
      65                 :             :  * PQcancelStart() and PQcancelPoll(), or the blocking PQcancelBlocking().
      66                 :             :  */
      67                 :             : PGcancelConn *
      68                 :           0 : PQcancelCreate(PGconn *conn)
      69                 :             : {
      70                 :           0 :         PGconn     *cancelConn = pqMakeEmptyPGconn();
      71                 :           0 :         pg_conn_host originalHost;
      72                 :             : 
      73         [ #  # ]:           0 :         if (cancelConn == NULL)
      74                 :           0 :                 return NULL;
      75                 :             : 
      76                 :             :         /* Check we have an open connection */
      77         [ #  # ]:           0 :         if (!conn)
      78                 :             :         {
      79                 :           0 :                 libpq_append_conn_error(cancelConn, "connection pointer is NULL");
      80                 :           0 :                 return (PGcancelConn *) cancelConn;
      81                 :             :         }
      82                 :             : 
      83         [ #  # ]:           0 :         if (conn->sock == PGINVALID_SOCKET)
      84                 :             :         {
      85                 :           0 :                 libpq_append_conn_error(cancelConn, "connection not open");
      86                 :           0 :                 return (PGcancelConn *) cancelConn;
      87                 :             :         }
      88                 :             : 
      89                 :             :         /* Check that we have received a cancellation key */
      90         [ #  # ]:           0 :         if (conn->be_cancel_key_len == 0)
      91                 :             :         {
      92                 :           0 :                 libpq_append_conn_error(cancelConn, "no cancellation key received");
      93                 :           0 :                 return (PGcancelConn *) cancelConn;
      94                 :             :         }
      95                 :             : 
      96                 :             :         /*
      97                 :             :          * Indicate that this connection is used to send a cancellation
      98                 :             :          */
      99                 :           0 :         cancelConn->cancelRequest = true;
     100                 :             : 
     101         [ #  # ]:           0 :         if (!pqCopyPGconn(conn, cancelConn))
     102                 :           0 :                 return (PGcancelConn *) cancelConn;
     103                 :             : 
     104                 :             :         /*
     105                 :             :          * Compute derived options
     106                 :             :          */
     107         [ #  # ]:           0 :         if (!pqConnectOptions2(cancelConn))
     108                 :           0 :                 return (PGcancelConn *) cancelConn;
     109                 :             : 
     110                 :             :         /*
     111                 :             :          * Copy cancellation token data from the original connection
     112                 :             :          */
     113                 :           0 :         cancelConn->be_pid = conn->be_pid;
     114         [ #  # ]:           0 :         if (conn->be_cancel_key != NULL)
     115                 :             :         {
     116                 :           0 :                 cancelConn->be_cancel_key = malloc(conn->be_cancel_key_len);
     117         [ #  # ]:           0 :                 if (cancelConn->be_cancel_key == NULL)
     118                 :           0 :                         goto oom_error;
     119                 :           0 :                 memcpy(cancelConn->be_cancel_key, conn->be_cancel_key, conn->be_cancel_key_len);
     120                 :           0 :         }
     121                 :           0 :         cancelConn->be_cancel_key_len = conn->be_cancel_key_len;
     122                 :           0 :         cancelConn->pversion = conn->pversion;
     123                 :             : 
     124                 :             :         /*
     125                 :             :          * Cancel requests should not iterate over all possible hosts. The request
     126                 :             :          * needs to be sent to the exact host and address that the original
     127                 :             :          * connection used. So we manually create the host and address arrays with
     128                 :             :          * a single element after freeing the host array that we generated from
     129                 :             :          * the connection options.
     130                 :             :          */
     131                 :           0 :         pqReleaseConnHosts(cancelConn);
     132                 :           0 :         cancelConn->nconnhost = 1;
     133                 :           0 :         cancelConn->naddr = 1;
     134                 :             : 
     135                 :           0 :         cancelConn->connhost = calloc(cancelConn->nconnhost, sizeof(pg_conn_host));
     136         [ #  # ]:           0 :         if (!cancelConn->connhost)
     137                 :           0 :                 goto oom_error;
     138                 :             : 
     139                 :           0 :         originalHost = conn->connhost[conn->whichhost];
     140                 :           0 :         cancelConn->connhost[0].type = originalHost.type;
     141         [ #  # ]:           0 :         if (originalHost.host)
     142                 :             :         {
     143                 :           0 :                 cancelConn->connhost[0].host = strdup(originalHost.host);
     144         [ #  # ]:           0 :                 if (!cancelConn->connhost[0].host)
     145                 :           0 :                         goto oom_error;
     146                 :           0 :         }
     147         [ #  # ]:           0 :         if (originalHost.hostaddr)
     148                 :             :         {
     149                 :           0 :                 cancelConn->connhost[0].hostaddr = strdup(originalHost.hostaddr);
     150         [ #  # ]:           0 :                 if (!cancelConn->connhost[0].hostaddr)
     151                 :           0 :                         goto oom_error;
     152                 :           0 :         }
     153         [ #  # ]:           0 :         if (originalHost.port)
     154                 :             :         {
     155                 :           0 :                 cancelConn->connhost[0].port = strdup(originalHost.port);
     156         [ #  # ]:           0 :                 if (!cancelConn->connhost[0].port)
     157                 :           0 :                         goto oom_error;
     158                 :           0 :         }
     159         [ #  # ]:           0 :         if (originalHost.password)
     160                 :             :         {
     161                 :           0 :                 cancelConn->connhost[0].password = strdup(originalHost.password);
     162         [ #  # ]:           0 :                 if (!cancelConn->connhost[0].password)
     163                 :           0 :                         goto oom_error;
     164                 :           0 :         }
     165                 :             : 
     166                 :           0 :         cancelConn->addr = calloc(cancelConn->naddr, sizeof(AddrInfo));
     167         [ #  # ]:           0 :         if (!cancelConn->addr)
     168                 :           0 :                 goto oom_error;
     169                 :             : 
     170                 :           0 :         cancelConn->addr[0].addr = conn->raddr;
     171                 :           0 :         cancelConn->addr[0].family = conn->raddr.addr.ss_family;
     172                 :             : 
     173                 :           0 :         cancelConn->status = CONNECTION_ALLOCATED;
     174                 :           0 :         return (PGcancelConn *) cancelConn;
     175                 :             : 
     176                 :             : oom_error:
     177                 :           0 :         cancelConn->status = CONNECTION_BAD;
     178                 :           0 :         libpq_append_conn_error(cancelConn, "out of memory");
     179                 :           0 :         return (PGcancelConn *) cancelConn;
     180                 :           0 : }
     181                 :             : 
     182                 :             : 
     183                 :             : /*
     184                 :             :  *              PQcancelBlocking
     185                 :             :  *
     186                 :             :  * Send a cancellation request in a blocking fashion.
     187                 :             :  * Returns 1 if successful 0 if not.
     188                 :             :  */
     189                 :             : int
     190                 :           0 : PQcancelBlocking(PGcancelConn *cancelConn)
     191                 :             : {
     192         [ #  # ]:           0 :         if (!PQcancelStart(cancelConn))
     193                 :           0 :                 return 0;
     194                 :           0 :         return pqConnectDBComplete(&cancelConn->conn);
     195                 :           0 : }
     196                 :             : 
     197                 :             : /*
     198                 :             :  *              PQcancelStart
     199                 :             :  *
     200                 :             :  * Starts sending a cancellation request in a non-blocking fashion. Returns
     201                 :             :  * 1 if successful 0 if not.
     202                 :             :  */
     203                 :             : int
     204                 :           0 : PQcancelStart(PGcancelConn *cancelConn)
     205                 :             : {
     206   [ #  #  #  # ]:           0 :         if (!cancelConn || cancelConn->conn.status == CONNECTION_BAD)
     207                 :           0 :                 return 0;
     208                 :             : 
     209         [ #  # ]:           0 :         if (cancelConn->conn.status != CONNECTION_ALLOCATED)
     210                 :             :         {
     211                 :           0 :                 libpq_append_conn_error(&cancelConn->conn,
     212                 :             :                                                                 "cancel request is already being sent on this connection");
     213                 :           0 :                 cancelConn->conn.status = CONNECTION_BAD;
     214                 :           0 :                 return 0;
     215                 :             :         }
     216                 :             : 
     217                 :           0 :         return pqConnectDBStart(&cancelConn->conn);
     218                 :           0 : }
     219                 :             : 
     220                 :             : /*
     221                 :             :  *              PQcancelPoll
     222                 :             :  *
     223                 :             :  * Poll a cancel connection. For usage details see PQconnectPoll.
     224                 :             :  */
     225                 :             : PostgresPollingStatusType
     226                 :           0 : PQcancelPoll(PGcancelConn *cancelConn)
     227                 :             : {
     228                 :           0 :         PGconn     *conn = &cancelConn->conn;
     229                 :           0 :         int                     n;
     230                 :             : 
     231                 :             :         /*
     232                 :             :          * We leave most of the connection establishment to PQconnectPoll, since
     233                 :             :          * it's very similar to normal connection establishment. But once we get
     234                 :             :          * to the CONNECTION_AWAITING_RESPONSE we need to start doing our own
     235                 :             :          * thing.
     236                 :             :          */
     237         [ #  # ]:           0 :         if (conn->status != CONNECTION_AWAITING_RESPONSE)
     238                 :             :         {
     239                 :           0 :                 return PQconnectPoll(conn);
     240                 :             :         }
     241                 :             : 
     242                 :             :         /*
     243                 :             :          * At this point we are waiting on the server to close the connection,
     244                 :             :          * which is its way of communicating that the cancel has been handled.
     245                 :             :          */
     246                 :             : 
     247                 :           0 :         n = pqReadData(conn);
     248                 :             : 
     249         [ #  # ]:           0 :         if (n == 0)
     250                 :           0 :                 return PGRES_POLLING_READING;
     251                 :             : 
     252                 :             : #ifndef WIN32
     253                 :             : 
     254                 :             :         /*
     255                 :             :          * If we receive an error report it, but only if errno is non-zero.
     256                 :             :          * Otherwise we assume it's an EOF, which is what we expect from the
     257                 :             :          * server.
     258                 :             :          *
     259                 :             :          * We skip this for Windows, because Windows is a bit special in its EOF
     260                 :             :          * behaviour for TCP. Sometimes it will error with an ECONNRESET when
     261                 :             :          * there is a clean connection closure. See these threads for details:
     262                 :             :          * https://www.postgresql.org/message-id/flat/90b34057-4176-7bb0-0dbb-9822a5f6425b%40greiz-reinsdorf.de
     263                 :             :          *
     264                 :             :          * https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BOeoETZQ%3DQw5Ub5h3tmwQhBmDA%3DnuNO3KG%3DzWfUypFAw%40mail.gmail.com
     265                 :             :          *
     266                 :             :          * PQcancel ignores such errors and reports success for the cancellation
     267                 :             :          * anyway, so even if this is not always correct we do the same here.
     268                 :             :          */
     269   [ #  #  #  # ]:           0 :         if (n < 0 && errno != 0)
     270                 :             :         {
     271                 :           0 :                 conn->status = CONNECTION_BAD;
     272                 :           0 :                 return PGRES_POLLING_FAILED;
     273                 :             :         }
     274                 :             : #endif
     275                 :             : 
     276                 :             :         /*
     277                 :             :          * We don't expect any data, only connection closure. So if we strangely
     278                 :             :          * do receive some data we consider that an error.
     279                 :             :          */
     280         [ #  # ]:           0 :         if (n > 0)
     281                 :             :         {
     282                 :           0 :                 libpq_append_conn_error(conn, "unexpected response from server");
     283                 :           0 :                 conn->status = CONNECTION_BAD;
     284                 :           0 :                 return PGRES_POLLING_FAILED;
     285                 :             :         }
     286                 :             : 
     287                 :             :         /*
     288                 :             :          * Getting here means that we received an EOF, which is what we were
     289                 :             :          * expecting -- the cancel request has completed.
     290                 :             :          */
     291                 :           0 :         cancelConn->conn.status = CONNECTION_OK;
     292                 :           0 :         resetPQExpBuffer(&conn->errorMessage);
     293                 :           0 :         return PGRES_POLLING_OK;
     294                 :           0 : }
     295                 :             : 
     296                 :             : /*
     297                 :             :  *              PQcancelStatus
     298                 :             :  *
     299                 :             :  * Get the status of a cancel connection.
     300                 :             :  */
     301                 :             : ConnStatusType
     302                 :           0 : PQcancelStatus(const PGcancelConn *cancelConn)
     303                 :             : {
     304                 :           0 :         return PQstatus(&cancelConn->conn);
     305                 :             : }
     306                 :             : 
     307                 :             : /*
     308                 :             :  *              PQcancelSocket
     309                 :             :  *
     310                 :             :  * Get the socket of the cancel connection.
     311                 :             :  */
     312                 :             : int
     313                 :           0 : PQcancelSocket(const PGcancelConn *cancelConn)
     314                 :             : {
     315                 :           0 :         return PQsocket(&cancelConn->conn);
     316                 :             : }
     317                 :             : 
     318                 :             : /*
     319                 :             :  *              PQcancelErrorMessage
     320                 :             :  *
     321                 :             :  * Returns the error message most recently generated by an operation on the
     322                 :             :  * cancel connection.
     323                 :             :  */
     324                 :             : char *
     325                 :           0 : PQcancelErrorMessage(const PGcancelConn *cancelConn)
     326                 :             : {
     327                 :           0 :         return PQerrorMessage(&cancelConn->conn);
     328                 :             : }
     329                 :             : 
     330                 :             : /*
     331                 :             :  *              PQcancelReset
     332                 :             :  *
     333                 :             :  * Resets the cancel connection, so it can be reused to send a new cancel
     334                 :             :  * request.
     335                 :             :  */
     336                 :             : void
     337                 :           0 : PQcancelReset(PGcancelConn *cancelConn)
     338                 :             : {
     339                 :           0 :         pqClosePGconn(&cancelConn->conn);
     340                 :           0 :         cancelConn->conn.status = CONNECTION_ALLOCATED;
     341                 :           0 :         cancelConn->conn.whichhost = 0;
     342                 :           0 :         cancelConn->conn.whichaddr = 0;
     343                 :           0 :         cancelConn->conn.try_next_host = false;
     344                 :           0 :         cancelConn->conn.try_next_addr = false;
     345                 :           0 : }
     346                 :             : 
     347                 :             : /*
     348                 :             :  *              PQcancelFinish
     349                 :             :  *
     350                 :             :  * Closes and frees the cancel connection.
     351                 :             :  */
     352                 :             : void
     353                 :           0 : PQcancelFinish(PGcancelConn *cancelConn)
     354                 :             : {
     355                 :           0 :         PQfinish(&cancelConn->conn);
     356                 :           0 : }
     357                 :             : 
     358                 :             : /*
     359                 :             :  * PQgetCancel: get a PGcancel structure corresponding to a connection.
     360                 :             :  *
     361                 :             :  * A copy is needed to be able to cancel a running query from a different
     362                 :             :  * thread. If the same structure is used all structure members would have
     363                 :             :  * to be individually locked (if the entire structure was locked, it would
     364                 :             :  * be impossible to cancel a synchronous query because the structure would
     365                 :             :  * have to stay locked for the duration of the query).
     366                 :             :  */
     367                 :             : PGcancel *
     368                 :       58324 : PQgetCancel(PGconn *conn)
     369                 :             : {
     370                 :       58324 :         PGcancel   *cancel;
     371                 :       58324 :         int                     cancel_req_len;
     372                 :       58324 :         CancelRequestPacket *req;
     373                 :             : 
     374         [ +  + ]:       58324 :         if (!conn)
     375                 :           7 :                 return NULL;
     376                 :             : 
     377         [ +  - ]:       58317 :         if (conn->sock == PGINVALID_SOCKET)
     378                 :           0 :                 return NULL;
     379                 :             : 
     380                 :             :         /* Check that we have received a cancellation key */
     381         [ +  - ]:       58317 :         if (conn->be_cancel_key_len == 0)
     382                 :             :         {
     383                 :             :                 /*
     384                 :             :                  * In case there is no cancel key, return an all-zero PGcancel object.
     385                 :             :                  * Actually calling PQcancel on this will fail, but we allow creating
     386                 :             :                  * the PGcancel object anyway. Arguably it would be better return NULL
     387                 :             :                  * to indicate that cancellation is not possible, but there'd be no
     388                 :             :                  * way for the caller to distinguish "out of memory" from "server did
     389                 :             :                  * not send a cancel key". Also, this is how PGgetCancel() has always
     390                 :             :                  * behaved, and if we changed it, some clients would stop working
     391                 :             :                  * altogether with servers that don't support cancellation. (The
     392                 :             :                  * modern PQcancelCreate() function returns a failed connection object
     393                 :             :                  * instead.)
     394                 :             :                  *
     395                 :             :                  * The returned dummy object has cancel_pkt_len == 0; we check for
     396                 :             :                  * that in PQcancel() to identify it as a dummy.
     397                 :             :                  */
     398                 :           0 :                 return calloc(1, sizeof(PGcancel));
     399                 :             :         }
     400                 :             : 
     401                 :       58317 :         cancel_req_len = offsetof(CancelRequestPacket, cancelAuthCode) + conn->be_cancel_key_len;
     402                 :       58317 :         cancel = malloc(offsetof(PGcancel, cancel_req) + cancel_req_len);
     403         [ +  - ]:       58317 :         if (cancel == NULL)
     404                 :           0 :                 return NULL;
     405                 :             : 
     406                 :       58317 :         memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
     407                 :             : 
     408                 :             :         /* We use -1 to indicate an unset connection option */
     409                 :       58317 :         cancel->pgtcp_user_timeout = -1;
     410                 :       58317 :         cancel->keepalives = -1;
     411                 :       58317 :         cancel->keepalives_idle = -1;
     412                 :       58317 :         cancel->keepalives_interval = -1;
     413                 :       58317 :         cancel->keepalives_count = -1;
     414         [ +  - ]:       58317 :         if (conn->pgtcp_user_timeout != NULL)
     415                 :             :         {
     416   [ #  #  #  # ]:           0 :                 if (!pqParseIntParam(conn->pgtcp_user_timeout,
     417                 :           0 :                                                          &cancel->pgtcp_user_timeout,
     418                 :           0 :                                                          conn, "tcp_user_timeout"))
     419                 :           0 :                         goto fail;
     420                 :           0 :         }
     421         [ +  - ]:       58317 :         if (conn->keepalives != NULL)
     422                 :             :         {
     423   [ #  #  #  # ]:           0 :                 if (!pqParseIntParam(conn->keepalives,
     424                 :           0 :                                                          &cancel->keepalives,
     425                 :           0 :                                                          conn, "keepalives"))
     426                 :           0 :                         goto fail;
     427                 :           0 :         }
     428         [ +  - ]:       58317 :         if (conn->keepalives_idle != NULL)
     429                 :             :         {
     430   [ #  #  #  # ]:           0 :                 if (!pqParseIntParam(conn->keepalives_idle,
     431                 :           0 :                                                          &cancel->keepalives_idle,
     432                 :           0 :                                                          conn, "keepalives_idle"))
     433                 :           0 :                         goto fail;
     434                 :           0 :         }
     435         [ +  - ]:       58317 :         if (conn->keepalives_interval != NULL)
     436                 :             :         {
     437   [ #  #  #  # ]:           0 :                 if (!pqParseIntParam(conn->keepalives_interval,
     438                 :           0 :                                                          &cancel->keepalives_interval,
     439                 :           0 :                                                          conn, "keepalives_interval"))
     440                 :           0 :                         goto fail;
     441                 :           0 :         }
     442         [ +  - ]:       58317 :         if (conn->keepalives_count != NULL)
     443                 :             :         {
     444   [ #  #  #  # ]:           0 :                 if (!pqParseIntParam(conn->keepalives_count,
     445                 :           0 :                                                          &cancel->keepalives_count,
     446                 :           0 :                                                          conn, "keepalives_count"))
     447                 :           0 :                         goto fail;
     448                 :           0 :         }
     449                 :             : 
     450                 :       58317 :         req = (CancelRequestPacket *) &cancel->cancel_req;
     451                 :       58317 :         req->cancelRequestCode = pg_hton32(CANCEL_REQUEST_CODE);
     452                 :       58317 :         req->backendPID = pg_hton32(conn->be_pid);
     453                 :       58317 :         memcpy(req->cancelAuthCode, conn->be_cancel_key, conn->be_cancel_key_len);
     454                 :             :         /* include the length field itself in the length */
     455                 :       58317 :         cancel->cancel_pkt_len = pg_hton32(cancel_req_len + 4);
     456                 :             : 
     457                 :       58317 :         return cancel;
     458                 :             : 
     459                 :             : fail:
     460                 :           0 :         free(cancel);
     461                 :           0 :         return NULL;
     462                 :       58324 : }
     463                 :             : 
     464                 :             : /*
     465                 :             :  * PQsendCancelRequest
     466                 :             :  *       Submit a CancelRequest message, but don't wait for it to finish
     467                 :             :  *
     468                 :             :  * Returns: 1 if successfully submitted
     469                 :             :  *                      0 if error (conn->errorMessage is set)
     470                 :             :  */
     471                 :             : int
     472                 :           0 : PQsendCancelRequest(PGconn *cancelConn)
     473                 :             : {
     474                 :           0 :         CancelRequestPacket req;
     475                 :             : 
     476                 :             :         /* Start the message. */
     477         [ #  # ]:           0 :         if (pqPutMsgStart(0, cancelConn))
     478                 :           0 :                 return STATUS_ERROR;
     479                 :             : 
     480                 :             :         /* Send the message body. */
     481                 :           0 :         memset(&req, 0, offsetof(CancelRequestPacket, cancelAuthCode));
     482                 :           0 :         req.cancelRequestCode = pg_hton32(CANCEL_REQUEST_CODE);
     483                 :           0 :         req.backendPID = pg_hton32(cancelConn->be_pid);
     484         [ #  # ]:           0 :         if (pqPutnchar(&req, offsetof(CancelRequestPacket, cancelAuthCode), cancelConn))
     485                 :           0 :                 return STATUS_ERROR;
     486         [ #  # ]:           0 :         if (pqPutnchar(cancelConn->be_cancel_key, cancelConn->be_cancel_key_len, cancelConn))
     487                 :           0 :                 return STATUS_ERROR;
     488                 :             : 
     489                 :             :         /* Finish the message. */
     490         [ #  # ]:           0 :         if (pqPutMsgEnd(cancelConn))
     491                 :           0 :                 return STATUS_ERROR;
     492                 :             : 
     493                 :             :         /* Flush to ensure backend gets it. */
     494         [ #  # ]:           0 :         if (pqFlush(cancelConn))
     495                 :           0 :                 return STATUS_ERROR;
     496                 :             : 
     497                 :           0 :         return STATUS_OK;
     498                 :           0 : }
     499                 :             : 
     500                 :             : /* PQfreeCancel: free a cancel structure */
     501                 :             : void
     502                 :       58317 : PQfreeCancel(PGcancel *cancel)
     503                 :             : {
     504                 :       58317 :         free(cancel);
     505                 :       58317 : }
     506                 :             : 
     507                 :             : 
     508                 :             : /*
     509                 :             :  * Sets an integer socket option on a TCP socket, if the provided value is
     510                 :             :  * not negative.  Returns false if setsockopt fails for some reason.
     511                 :             :  *
     512                 :             :  * CAUTION: This needs to be signal safe, since it's used by PQcancel.
     513                 :             :  */
     514                 :             : #if defined(TCP_USER_TIMEOUT) || !defined(WIN32)
     515                 :             : static bool
     516                 :           0 : optional_setsockopt(int fd, int protoid, int optid, int value)
     517                 :             : {
     518         [ #  # ]:           0 :         if (value < 0)
     519                 :           0 :                 return true;
     520         [ #  # ]:           0 :         if (setsockopt(fd, protoid, optid, (char *) &value, sizeof(value)) < 0)
     521                 :           0 :                 return false;
     522                 :           0 :         return true;
     523                 :           0 : }
     524                 :             : #endif
     525                 :             : 
     526                 :             : 
     527                 :             : /*
     528                 :             :  * PQcancel: old, non-encrypted, but signal-safe way of requesting query cancel
     529                 :             :  *
     530                 :             :  * The return value is true if the cancel request was successfully
     531                 :             :  * dispatched, false if not (in which case an error message is available).
     532                 :             :  * Note: successful dispatch is no guarantee that there will be any effect at
     533                 :             :  * the backend.  The application must read the operation result as usual.
     534                 :             :  *
     535                 :             :  * On failure, an error message is stored in *errbuf, which must be of size
     536                 :             :  * errbufsize (recommended size is 256 bytes).  *errbuf is not changed on
     537                 :             :  * success return.
     538                 :             :  *
     539                 :             :  * CAUTION: we want this routine to be safely callable from a signal handler
     540                 :             :  * (for example, an application might want to call it in a SIGINT handler).
     541                 :             :  * This means we cannot use any C library routine that might be non-reentrant.
     542                 :             :  * malloc/free are often non-reentrant, and anything that might call them is
     543                 :             :  * just as dangerous.  We avoid sprintf here for that reason.  Building up
     544                 :             :  * error messages with strcpy/strcat is tedious but should be quite safe.
     545                 :             :  * We also save/restore errno in case the signal handler support doesn't.
     546                 :             :  */
     547                 :             : int
     548                 :           0 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
     549                 :             : {
     550                 :           0 :         int                     save_errno = SOCK_ERRNO;
     551                 :           0 :         pgsocket        tmpsock = PGINVALID_SOCKET;
     552                 :           0 :         int                     maxlen;
     553                 :           0 :         char            recvbuf;
     554                 :           0 :         int                     cancel_pkt_len;
     555                 :             : 
     556         [ #  # ]:           0 :         if (!cancel)
     557                 :             :         {
     558                 :           0 :                 strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
     559                 :             :                 /* strlcpy probably doesn't change errno, but be paranoid */
     560                 :           0 :                 SOCK_ERRNO_SET(save_errno);
     561                 :           0 :                 return false;
     562                 :             :         }
     563                 :             : 
     564         [ #  # ]:           0 :         if (cancel->cancel_pkt_len == 0)
     565                 :             :         {
     566                 :             :                 /* This is a dummy PGcancel object, see PQgetCancel */
     567                 :           0 :                 strlcpy(errbuf, "PQcancel() -- no cancellation key received", errbufsize);
     568                 :             :                 /* strlcpy probably doesn't change errno, but be paranoid */
     569                 :           0 :                 SOCK_ERRNO_SET(save_errno);
     570                 :           0 :                 return false;
     571                 :             :         }
     572                 :             : 
     573                 :             :         /*
     574                 :             :          * We need to open a temporary connection to the postmaster. Do this with
     575                 :             :          * only kernel calls.
     576                 :             :          */
     577         [ #  # ]:           0 :         if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
     578                 :             :         {
     579                 :           0 :                 strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
     580                 :           0 :                 goto cancel_errReturn;
     581                 :             :         }
     582                 :             : 
     583                 :             :         /*
     584                 :             :          * Since this connection will only be used to send a single packet of
     585                 :             :          * data, we don't need NODELAY.  We also don't set the socket to
     586                 :             :          * nonblocking mode, because the API definition of PQcancel requires the
     587                 :             :          * cancel to be sent in a blocking way.
     588                 :             :          *
     589                 :             :          * We do set socket options related to keepalives and other TCP timeouts.
     590                 :             :          * This ensures that this function does not block indefinitely when
     591                 :             :          * reasonable keepalive and timeout settings have been provided.
     592                 :             :          */
     593   [ #  #  #  # ]:           0 :         if (cancel->raddr.addr.ss_family != AF_UNIX &&
     594                 :           0 :                 cancel->keepalives != 0)
     595                 :             :         {
     596                 :             : #ifndef WIN32
     597         [ #  # ]:           0 :                 if (!optional_setsockopt(tmpsock, SOL_SOCKET, SO_KEEPALIVE, 1))
     598                 :             :                 {
     599                 :           0 :                         strlcpy(errbuf, "PQcancel() -- setsockopt(SO_KEEPALIVE) failed: ", errbufsize);
     600                 :           0 :                         goto cancel_errReturn;
     601                 :             :                 }
     602                 :             : 
     603                 :             : #ifdef PG_TCP_KEEPALIVE_IDLE
     604                 :             :                 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
     605                 :             :                                                                  cancel->keepalives_idle))
     606                 :             :                 {
     607                 :             :                         strlcpy(errbuf, "PQcancel() -- setsockopt(" PG_TCP_KEEPALIVE_IDLE_STR ") failed: ", errbufsize);
     608                 :             :                         goto cancel_errReturn;
     609                 :             :                 }
     610                 :             : #endif
     611                 :             : 
     612                 :             : #ifdef TCP_KEEPINTVL
     613                 :             :                 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPINTVL,
     614                 :             :                                                                  cancel->keepalives_interval))
     615                 :             :                 {
     616                 :             :                         strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPINTVL) failed: ", errbufsize);
     617                 :             :                         goto cancel_errReturn;
     618                 :             :                 }
     619                 :             : #endif
     620                 :             : 
     621                 :             : #ifdef TCP_KEEPCNT
     622                 :             :                 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPCNT,
     623                 :             :                                                                  cancel->keepalives_count))
     624                 :             :                 {
     625                 :             :                         strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPCNT) failed: ", errbufsize);
     626                 :             :                         goto cancel_errReturn;
     627                 :             :                 }
     628                 :             : #endif
     629                 :             : 
     630                 :             : #else                                                   /* WIN32 */
     631                 :             : 
     632                 :             : #ifdef SIO_KEEPALIVE_VALS
     633                 :             :                 if (!pqSetKeepalivesWin32(tmpsock,
     634                 :             :                                                                   cancel->keepalives_idle,
     635                 :             :                                                                   cancel->keepalives_interval))
     636                 :             :                 {
     637                 :             :                         strlcpy(errbuf, "PQcancel() -- WSAIoctl(SIO_KEEPALIVE_VALS) failed: ", errbufsize);
     638                 :             :                         goto cancel_errReturn;
     639                 :             :                 }
     640                 :             : #endif                                                  /* SIO_KEEPALIVE_VALS */
     641                 :             : #endif                                                  /* WIN32 */
     642                 :             : 
     643                 :             :                 /* TCP_USER_TIMEOUT works the same way on Unix and Windows */
     644                 :             : #ifdef TCP_USER_TIMEOUT
     645                 :             :                 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_USER_TIMEOUT,
     646                 :             :                                                                  cancel->pgtcp_user_timeout))
     647                 :             :                 {
     648                 :             :                         strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_USER_TIMEOUT) failed: ", errbufsize);
     649                 :             :                         goto cancel_errReturn;
     650                 :             :                 }
     651                 :             : #endif
     652                 :           0 :         }
     653                 :             : 
     654                 :             : retry3:
     655                 :           0 :         if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
     656   [ #  #  #  # ]:           0 :                                 cancel->raddr.salen) < 0)
     657                 :             :         {
     658         [ #  # ]:           0 :                 if (SOCK_ERRNO == EINTR)
     659                 :             :                         /* Interrupted system call - we'll just try again */
     660                 :           0 :                         goto retry3;
     661                 :           0 :                 strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
     662                 :           0 :                 goto cancel_errReturn;
     663                 :             :         }
     664                 :             : 
     665                 :           0 :         cancel_pkt_len = pg_ntoh32(cancel->cancel_pkt_len);
     666                 :             : 
     667                 :             : retry4:
     668                 :             : 
     669                 :             :         /*
     670                 :             :          * Send the cancel request packet. It starts with the message length at
     671                 :             :          * cancel_pkt_len, followed by the actual packet.
     672                 :             :          */
     673         [ #  # ]:           0 :         if (send(tmpsock, (char *) &cancel->cancel_pkt_len, cancel_pkt_len, 0) != cancel_pkt_len)
     674                 :             :         {
     675         [ #  # ]:           0 :                 if (SOCK_ERRNO == EINTR)
     676                 :             :                         /* Interrupted system call - we'll just try again */
     677                 :           0 :                         goto retry4;
     678                 :           0 :                 strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
     679                 :           0 :                 goto cancel_errReturn;
     680                 :             :         }
     681                 :             : 
     682                 :             :         /*
     683                 :             :          * Wait for the postmaster to close the connection, which indicates that
     684                 :             :          * it's processed the request.  Without this delay, we might issue another
     685                 :             :          * command only to find that our cancel zaps that command instead of the
     686                 :             :          * one we thought we were canceling.  Note we don't actually expect this
     687                 :             :          * read to obtain any data, we are just waiting for EOF to be signaled.
     688                 :             :          */
     689                 :             : retry5:
     690         [ #  # ]:           0 :         if (recv(tmpsock, &recvbuf, 1, 0) < 0)
     691                 :             :         {
     692         [ #  # ]:           0 :                 if (SOCK_ERRNO == EINTR)
     693                 :             :                         /* Interrupted system call - we'll just try again */
     694                 :           0 :                         goto retry5;
     695                 :             :                 /* we ignore other error conditions */
     696                 :           0 :         }
     697                 :             : 
     698                 :             :         /* All done */
     699                 :           0 :         closesocket(tmpsock);
     700                 :           0 :         SOCK_ERRNO_SET(save_errno);
     701                 :           0 :         return true;
     702                 :             : 
     703                 :             : cancel_errReturn:
     704                 :             : 
     705                 :             :         /*
     706                 :             :          * Make sure we don't overflow the error buffer. Leave space for the \n at
     707                 :             :          * the end, and for the terminating zero.
     708                 :             :          */
     709                 :           0 :         maxlen = errbufsize - strlen(errbuf) - 2;
     710         [ #  # ]:           0 :         if (maxlen >= 0)
     711                 :             :         {
     712                 :             :                 /*
     713                 :             :                  * We can't invoke strerror here, since it's not signal-safe.  Settle
     714                 :             :                  * for printing the decimal value of errno.  Even that has to be done
     715                 :             :                  * the hard way.
     716                 :             :                  */
     717                 :           0 :                 int                     val = SOCK_ERRNO;
     718                 :           0 :                 char            buf[32];
     719                 :           0 :                 char       *bufp;
     720                 :             : 
     721                 :           0 :                 bufp = buf + sizeof(buf) - 1;
     722                 :           0 :                 *bufp = '\0';
     723                 :           0 :                 do
     724                 :             :                 {
     725                 :           0 :                         *(--bufp) = (val % 10) + '0';
     726                 :           0 :                         val /= 10;
     727         [ #  # ]:           0 :                 } while (val > 0);
     728                 :           0 :                 bufp -= 6;
     729                 :           0 :                 memcpy(bufp, "error ", 6);
     730                 :           0 :                 strncat(errbuf, bufp, maxlen);
     731                 :           0 :                 strcat(errbuf, "\n");
     732                 :           0 :         }
     733         [ #  # ]:           0 :         if (tmpsock != PGINVALID_SOCKET)
     734                 :           0 :                 closesocket(tmpsock);
     735                 :           0 :         SOCK_ERRNO_SET(save_errno);
     736                 :           0 :         return false;
     737                 :           0 : }
     738                 :             : 
     739                 :             : /*
     740                 :             :  * PQrequestCancel: old, not thread-safe function for requesting query cancel
     741                 :             :  *
     742                 :             :  * Returns true if able to send the cancel request, false if not.
     743                 :             :  *
     744                 :             :  * On failure, the error message is saved in conn->errorMessage; this means
     745                 :             :  * that this can't be used when there might be other active operations on
     746                 :             :  * the connection object.
     747                 :             :  *
     748                 :             :  * NOTE: error messages will be cut off at the current size of the
     749                 :             :  * error message buffer, since we dare not try to expand conn->errorMessage!
     750                 :             :  */
     751                 :             : int
     752                 :           0 : PQrequestCancel(PGconn *conn)
     753                 :             : {
     754                 :           0 :         int                     r;
     755                 :           0 :         PGcancel   *cancel;
     756                 :             : 
     757                 :             :         /* Check we have an open connection */
     758         [ #  # ]:           0 :         if (!conn)
     759                 :           0 :                 return false;
     760                 :             : 
     761         [ #  # ]:           0 :         if (conn->sock == PGINVALID_SOCKET)
     762                 :             :         {
     763                 :           0 :                 strlcpy(conn->errorMessage.data,
     764                 :             :                                 "PQrequestCancel() -- connection is not open\n",
     765                 :             :                                 conn->errorMessage.maxlen);
     766                 :           0 :                 conn->errorMessage.len = strlen(conn->errorMessage.data);
     767                 :           0 :                 conn->errorReported = 0;
     768                 :             : 
     769                 :           0 :                 return false;
     770                 :             :         }
     771                 :             : 
     772                 :           0 :         cancel = PQgetCancel(conn);
     773         [ #  # ]:           0 :         if (cancel)
     774                 :             :         {
     775                 :           0 :                 r = PQcancel(cancel, conn->errorMessage.data,
     776                 :           0 :                                          conn->errorMessage.maxlen);
     777                 :           0 :                 PQfreeCancel(cancel);
     778                 :           0 :         }
     779                 :             :         else
     780                 :             :         {
     781                 :           0 :                 strlcpy(conn->errorMessage.data, "out of memory",
     782                 :             :                                 conn->errorMessage.maxlen);
     783                 :           0 :                 r = false;
     784                 :             :         }
     785                 :             : 
     786         [ #  # ]:           0 :         if (!r)
     787                 :             :         {
     788                 :           0 :                 conn->errorMessage.len = strlen(conn->errorMessage.data);
     789                 :           0 :                 conn->errorReported = 0;
     790                 :           0 :         }
     791                 :             : 
     792                 :           0 :         return r;
     793                 :           0 : }
        

Generated by: LCOV version 2.3.2-1