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