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