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, "connection pointer is NULL");
77 0 : return (PGcancelConn *) cancelConn;
78 : }
79 :
80 8 : if (conn->sock == PGINVALID_SOCKET)
81 : {
82 0 : libpq_append_conn_error(cancelConn, "connection 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->addr)
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 : cancelConn->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, "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 : * Returns the error message most recently generated by an operation on the
303 : * cancel connection.
304 : */
305 : char *
306 0 : PQcancelErrorMessage(const PGcancelConn *cancelConn)
307 : {
308 0 : return PQerrorMessage(&cancelConn->conn);
309 : }
310 :
311 : /*
312 : * PQcancelReset
313 : *
314 : * Resets the cancel connection, so it can be reused to send a new cancel
315 : * request.
316 : */
317 : void
318 2 : PQcancelReset(PGcancelConn *cancelConn)
319 : {
320 2 : pqClosePGconn(&cancelConn->conn);
321 2 : cancelConn->conn.status = CONNECTION_ALLOCATED;
322 2 : cancelConn->conn.whichhost = 0;
323 2 : cancelConn->conn.whichaddr = 0;
324 2 : cancelConn->conn.try_next_host = false;
325 2 : cancelConn->conn.try_next_addr = false;
326 2 : }
327 :
328 : /*
329 : * PQcancelFinish
330 : *
331 : * Closes and frees the cancel connection.
332 : */
333 : void
334 8 : PQcancelFinish(PGcancelConn *cancelConn)
335 : {
336 8 : PQfinish(&cancelConn->conn);
337 8 : }
338 :
339 : /*
340 : * PQgetCancel: get a PGcancel structure corresponding to a connection.
341 : *
342 : * A copy is needed to be able to cancel a running query from a different
343 : * thread. If the same structure is used all structure members would have
344 : * to be individually locked (if the entire structure was locked, it would
345 : * be impossible to cancel a synchronous query because the structure would
346 : * have to stay locked for the duration of the query).
347 : */
348 : PGcancel *
349 387042 : PQgetCancel(PGconn *conn)
350 : {
351 : PGcancel *cancel;
352 :
353 387042 : if (!conn)
354 44 : return NULL;
355 :
356 386998 : if (conn->sock == PGINVALID_SOCKET)
357 0 : return NULL;
358 :
359 386998 : cancel = malloc(sizeof(PGcancel));
360 386998 : if (cancel == NULL)
361 0 : return NULL;
362 :
363 386998 : memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
364 386998 : cancel->be_pid = conn->be_pid;
365 386998 : cancel->be_key = conn->be_key;
366 : /* We use -1 to indicate an unset connection option */
367 386998 : cancel->pgtcp_user_timeout = -1;
368 386998 : cancel->keepalives = -1;
369 386998 : cancel->keepalives_idle = -1;
370 386998 : cancel->keepalives_interval = -1;
371 386998 : cancel->keepalives_count = -1;
372 386998 : if (conn->pgtcp_user_timeout != NULL)
373 : {
374 0 : if (!pqParseIntParam(conn->pgtcp_user_timeout,
375 : &cancel->pgtcp_user_timeout,
376 : conn, "tcp_user_timeout"))
377 0 : goto fail;
378 : }
379 386998 : if (conn->keepalives != NULL)
380 : {
381 0 : if (!pqParseIntParam(conn->keepalives,
382 : &cancel->keepalives,
383 : conn, "keepalives"))
384 0 : goto fail;
385 : }
386 386998 : if (conn->keepalives_idle != NULL)
387 : {
388 0 : if (!pqParseIntParam(conn->keepalives_idle,
389 : &cancel->keepalives_idle,
390 : conn, "keepalives_idle"))
391 0 : goto fail;
392 : }
393 386998 : if (conn->keepalives_interval != NULL)
394 : {
395 0 : if (!pqParseIntParam(conn->keepalives_interval,
396 : &cancel->keepalives_interval,
397 : conn, "keepalives_interval"))
398 0 : goto fail;
399 : }
400 386998 : if (conn->keepalives_count != NULL)
401 : {
402 0 : if (!pqParseIntParam(conn->keepalives_count,
403 : &cancel->keepalives_count,
404 : conn, "keepalives_count"))
405 0 : goto fail;
406 : }
407 :
408 386998 : return cancel;
409 :
410 0 : fail:
411 0 : free(cancel);
412 0 : return NULL;
413 : }
414 :
415 : /* PQfreeCancel: free a cancel structure */
416 : void
417 386972 : PQfreeCancel(PGcancel *cancel)
418 : {
419 386972 : free(cancel);
420 386972 : }
421 :
422 :
423 : /*
424 : * Sets an integer socket option on a TCP socket, if the provided value is
425 : * not negative. Returns false if setsockopt fails for some reason.
426 : *
427 : * CAUTION: This needs to be signal safe, since it's used by PQcancel.
428 : */
429 : #if defined(TCP_USER_TIMEOUT) || !defined(WIN32)
430 : static bool
431 0 : optional_setsockopt(int fd, int protoid, int optid, int value)
432 : {
433 0 : if (value < 0)
434 0 : return true;
435 0 : if (setsockopt(fd, protoid, optid, (char *) &value, sizeof(value)) < 0)
436 0 : return false;
437 0 : return true;
438 : }
439 : #endif
440 :
441 :
442 : /*
443 : * PQcancel: old, non-encrypted, but signal-safe way of requesting query cancel
444 : *
445 : * The return value is true if the cancel request was successfully
446 : * dispatched, false if not (in which case an error message is available).
447 : * Note: successful dispatch is no guarantee that there will be any effect at
448 : * the backend. The application must read the operation result as usual.
449 : *
450 : * On failure, an error message is stored in *errbuf, which must be of size
451 : * errbufsize (recommended size is 256 bytes). *errbuf is not changed on
452 : * success return.
453 : *
454 : * CAUTION: we want this routine to be safely callable from a signal handler
455 : * (for example, an application might want to call it in a SIGINT handler).
456 : * This means we cannot use any C library routine that might be non-reentrant.
457 : * malloc/free are often non-reentrant, and anything that might call them is
458 : * just as dangerous. We avoid sprintf here for that reason. Building up
459 : * error messages with strcpy/strcat is tedious but should be quite safe.
460 : * We also save/restore errno in case the signal handler support doesn't.
461 : */
462 : int
463 8 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
464 : {
465 8 : int save_errno = SOCK_ERRNO;
466 8 : pgsocket tmpsock = PGINVALID_SOCKET;
467 : int maxlen;
468 : struct
469 : {
470 : uint32 packetlen;
471 : CancelRequestPacket cp;
472 : } crp;
473 :
474 8 : if (!cancel)
475 : {
476 0 : strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
477 : /* strlcpy probably doesn't change errno, but be paranoid */
478 0 : SOCK_ERRNO_SET(save_errno);
479 0 : return false;
480 : }
481 :
482 : /*
483 : * We need to open a temporary connection to the postmaster. Do this with
484 : * only kernel calls.
485 : */
486 8 : if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
487 : {
488 0 : strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
489 0 : goto cancel_errReturn;
490 : }
491 :
492 : /*
493 : * Since this connection will only be used to send a single packet of
494 : * data, we don't need NODELAY. We also don't set the socket to
495 : * nonblocking mode, because the API definition of PQcancel requires the
496 : * cancel to be sent in a blocking way.
497 : *
498 : * We do set socket options related to keepalives and other TCP timeouts.
499 : * This ensures that this function does not block indefinitely when
500 : * reasonable keepalive and timeout settings have been provided.
501 : */
502 8 : if (cancel->raddr.addr.ss_family != AF_UNIX &&
503 0 : cancel->keepalives != 0)
504 : {
505 : #ifndef WIN32
506 0 : if (!optional_setsockopt(tmpsock, SOL_SOCKET, SO_KEEPALIVE, 1))
507 : {
508 0 : strlcpy(errbuf, "PQcancel() -- setsockopt(SO_KEEPALIVE) failed: ", errbufsize);
509 0 : goto cancel_errReturn;
510 : }
511 :
512 : #ifdef PG_TCP_KEEPALIVE_IDLE
513 : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
514 : cancel->keepalives_idle))
515 : {
516 : strlcpy(errbuf, "PQcancel() -- setsockopt(" PG_TCP_KEEPALIVE_IDLE_STR ") failed: ", errbufsize);
517 : goto cancel_errReturn;
518 : }
519 : #endif
520 :
521 : #ifdef TCP_KEEPINTVL
522 : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPINTVL,
523 : cancel->keepalives_interval))
524 : {
525 : strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPINTVL) failed: ", errbufsize);
526 : goto cancel_errReturn;
527 : }
528 : #endif
529 :
530 : #ifdef TCP_KEEPCNT
531 : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPCNT,
532 : cancel->keepalives_count))
533 : {
534 : strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPCNT) failed: ", errbufsize);
535 : goto cancel_errReturn;
536 : }
537 : #endif
538 :
539 : #else /* WIN32 */
540 :
541 : #ifdef SIO_KEEPALIVE_VALS
542 : if (!pqSetKeepalivesWin32(tmpsock,
543 : cancel->keepalives_idle,
544 : cancel->keepalives_interval))
545 : {
546 : strlcpy(errbuf, "PQcancel() -- WSAIoctl(SIO_KEEPALIVE_VALS) failed: ", errbufsize);
547 : goto cancel_errReturn;
548 : }
549 : #endif /* SIO_KEEPALIVE_VALS */
550 : #endif /* WIN32 */
551 :
552 : /* TCP_USER_TIMEOUT works the same way on Unix and Windows */
553 : #ifdef TCP_USER_TIMEOUT
554 : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_USER_TIMEOUT,
555 : cancel->pgtcp_user_timeout))
556 : {
557 : strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_USER_TIMEOUT) failed: ", errbufsize);
558 : goto cancel_errReturn;
559 : }
560 : #endif
561 : }
562 :
563 8 : retry3:
564 8 : if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
565 : cancel->raddr.salen) < 0)
566 : {
567 0 : if (SOCK_ERRNO == EINTR)
568 : /* Interrupted system call - we'll just try again */
569 0 : goto retry3;
570 0 : strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
571 0 : goto cancel_errReturn;
572 : }
573 :
574 : /* Create and send the cancel request packet. */
575 :
576 8 : crp.packetlen = pg_hton32((uint32) sizeof(crp));
577 8 : crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
578 8 : crp.cp.backendPID = pg_hton32(cancel->be_pid);
579 8 : crp.cp.cancelAuthCode = pg_hton32(cancel->be_key);
580 :
581 8 : retry4:
582 8 : if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
583 : {
584 0 : if (SOCK_ERRNO == EINTR)
585 : /* Interrupted system call - we'll just try again */
586 0 : goto retry4;
587 0 : strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
588 0 : goto cancel_errReturn;
589 : }
590 :
591 : /*
592 : * Wait for the postmaster to close the connection, which indicates that
593 : * it's processed the request. Without this delay, we might issue another
594 : * command only to find that our cancel zaps that command instead of the
595 : * one we thought we were canceling. Note we don't actually expect this
596 : * read to obtain any data, we are just waiting for EOF to be signaled.
597 : */
598 8 : retry5:
599 8 : if (recv(tmpsock, (char *) &crp, 1, 0) < 0)
600 : {
601 0 : if (SOCK_ERRNO == EINTR)
602 : /* Interrupted system call - we'll just try again */
603 0 : goto retry5;
604 : /* we ignore other error conditions */
605 : }
606 :
607 : /* All done */
608 8 : closesocket(tmpsock);
609 8 : SOCK_ERRNO_SET(save_errno);
610 8 : return true;
611 :
612 0 : cancel_errReturn:
613 :
614 : /*
615 : * Make sure we don't overflow the error buffer. Leave space for the \n at
616 : * the end, and for the terminating zero.
617 : */
618 0 : maxlen = errbufsize - strlen(errbuf) - 2;
619 0 : if (maxlen >= 0)
620 : {
621 : /*
622 : * We can't invoke strerror here, since it's not signal-safe. Settle
623 : * for printing the decimal value of errno. Even that has to be done
624 : * the hard way.
625 : */
626 0 : int val = SOCK_ERRNO;
627 : char buf[32];
628 : char *bufp;
629 :
630 0 : bufp = buf + sizeof(buf) - 1;
631 0 : *bufp = '\0';
632 : do
633 : {
634 0 : *(--bufp) = (val % 10) + '0';
635 0 : val /= 10;
636 0 : } while (val > 0);
637 0 : bufp -= 6;
638 0 : memcpy(bufp, "error ", 6);
639 0 : strncat(errbuf, bufp, maxlen);
640 0 : strcat(errbuf, "\n");
641 : }
642 0 : if (tmpsock != PGINVALID_SOCKET)
643 0 : closesocket(tmpsock);
644 0 : SOCK_ERRNO_SET(save_errno);
645 0 : return false;
646 : }
647 :
648 : /*
649 : * PQrequestCancel: old, not thread-safe function for requesting query cancel
650 : *
651 : * Returns true if able to send the cancel request, false if not.
652 : *
653 : * On failure, the error message is saved in conn->errorMessage; this means
654 : * that this can't be used when there might be other active operations on
655 : * the connection object.
656 : *
657 : * NOTE: error messages will be cut off at the current size of the
658 : * error message buffer, since we dare not try to expand conn->errorMessage!
659 : */
660 : int
661 2 : PQrequestCancel(PGconn *conn)
662 : {
663 : int r;
664 : PGcancel *cancel;
665 :
666 : /* Check we have an open connection */
667 2 : if (!conn)
668 0 : return false;
669 :
670 2 : if (conn->sock == PGINVALID_SOCKET)
671 : {
672 0 : strlcpy(conn->errorMessage.data,
673 : "PQrequestCancel() -- connection is not open\n",
674 : conn->errorMessage.maxlen);
675 0 : conn->errorMessage.len = strlen(conn->errorMessage.data);
676 0 : conn->errorReported = 0;
677 :
678 0 : return false;
679 : }
680 :
681 2 : cancel = PQgetCancel(conn);
682 2 : if (cancel)
683 : {
684 2 : r = PQcancel(cancel, conn->errorMessage.data,
685 2 : conn->errorMessage.maxlen);
686 2 : PQfreeCancel(cancel);
687 : }
688 : else
689 : {
690 0 : strlcpy(conn->errorMessage.data, "out of memory",
691 : conn->errorMessage.maxlen);
692 0 : r = false;
693 : }
694 :
695 2 : if (!r)
696 : {
697 0 : conn->errorMessage.len = strlen(conn->errorMessage.data);
698 0 : conn->errorReported = 0;
699 : }
700 :
701 2 : return r;
702 : }
|