Line data Source code
1 : /*
2 : * psql - the PostgreSQL interactive terminal
3 : *
4 : * Copyright (c) 2000-2017, PostgreSQL Global Development Group
5 : *
6 : * src/bin/psql/common.c
7 : */
8 : #include "postgres_fe.h"
9 : #include "common.h"
10 :
11 : #include <ctype.h>
12 : #include <limits.h>
13 : #include <math.h>
14 : #include <signal.h>
15 : #ifndef WIN32
16 : #include <unistd.h> /* for write() */
17 : #else
18 : #include <io.h> /* for _write() */
19 : #include <win32.h>
20 : #endif
21 :
22 : #include "fe_utils/string_utils.h"
23 : #include "portability/instr_time.h"
24 :
25 : #include "settings.h"
26 : #include "command.h"
27 : #include "copy.h"
28 : #include "crosstabview.h"
29 : #include "fe_utils/mbprint.h"
30 :
31 :
32 : static bool ExecQueryUsingCursor(const char *query, double *elapsed_msec);
33 : static bool command_no_begin(const char *query);
34 : static bool is_select_command(const char *query);
35 :
36 :
37 : /*
38 : * openQueryOutputFile --- attempt to open a query output file
39 : *
40 : * fname == NULL selects stdout, else an initial '|' selects a pipe,
41 : * else plain file.
42 : *
43 : * Returns output file pointer into *fout, and is-a-pipe flag into *is_pipe.
44 : * Caller is responsible for adjusting SIGPIPE state if it's a pipe.
45 : *
46 : * On error, reports suitable error message and returns FALSE.
47 : */
48 : bool
49 182 : openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe)
50 : {
51 182 : if (!fname || fname[0] == '\0')
52 : {
53 182 : *fout = stdout;
54 182 : *is_pipe = false;
55 : }
56 0 : else if (*fname == '|')
57 : {
58 0 : *fout = popen(fname + 1, "w");
59 0 : *is_pipe = true;
60 : }
61 : else
62 : {
63 0 : *fout = fopen(fname, "w");
64 0 : *is_pipe = false;
65 : }
66 :
67 182 : if (*fout == NULL)
68 : {
69 0 : psql_error("%s: %s\n", fname, strerror(errno));
70 0 : return false;
71 : }
72 :
73 182 : return true;
74 : }
75 :
76 : /*
77 : * setQFout
78 : * -- handler for -o command line option and \o command
79 : *
80 : * On success, updates pset with the new output file and returns true.
81 : * On failure, returns false without changing pset state.
82 : */
83 : bool
84 182 : setQFout(const char *fname)
85 : {
86 : FILE *fout;
87 : bool is_pipe;
88 :
89 : /* First make sure we can open the new output file/pipe */
90 182 : if (!openQueryOutputFile(fname, &fout, &is_pipe))
91 0 : return false;
92 :
93 : /* Close old file/pipe */
94 182 : if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
95 : {
96 0 : if (pset.queryFoutPipe)
97 0 : pclose(pset.queryFout);
98 : else
99 0 : fclose(pset.queryFout);
100 : }
101 :
102 182 : pset.queryFout = fout;
103 182 : pset.queryFoutPipe = is_pipe;
104 :
105 : /* Adjust SIGPIPE handling appropriately: ignore signal if is_pipe */
106 182 : set_sigpipe_trap_state(is_pipe);
107 182 : restore_sigpipe_trap();
108 :
109 182 : return true;
110 : }
111 :
112 :
113 : /*
114 : * Variable-fetching callback for flex lexer
115 : *
116 : * If the specified variable exists, return its value as a string (malloc'd
117 : * and expected to be freed by the caller); else return NULL.
118 : *
119 : * If "quote" isn't PQUOTE_PLAIN, then return the value suitably quoted and
120 : * escaped for the specified quoting requirement. (Failure in escaping
121 : * should lead to printing an error and returning NULL.)
122 : *
123 : * "passthrough" is the pointer previously given to psql_scan_set_passthrough.
124 : * In psql, passthrough points to a ConditionalStack, which we check to
125 : * determine whether variable expansion is allowed.
126 : */
127 : char *
128 116 : psql_get_variable(const char *varname, PsqlScanQuoteType quote,
129 : void *passthrough)
130 : {
131 116 : char *result = NULL;
132 : const char *value;
133 :
134 : /* In an inactive \if branch, suppress all variable substitutions */
135 116 : if (passthrough && !conditional_active((ConditionalStack) passthrough))
136 10 : return NULL;
137 :
138 106 : value = GetVariable(pset.vars, varname);
139 106 : if (!value)
140 59 : return NULL;
141 :
142 47 : switch (quote)
143 : {
144 : case PQUOTE_PLAIN:
145 44 : result = pg_strdup(value);
146 44 : break;
147 : case PQUOTE_SQL_LITERAL:
148 : case PQUOTE_SQL_IDENT:
149 : {
150 : /*
151 : * For these cases, we use libpq's quoting functions, which
152 : * assume the string is in the connection's client encoding.
153 : */
154 : char *escaped_value;
155 :
156 3 : if (!pset.db)
157 : {
158 0 : psql_error("cannot escape without active connection\n");
159 0 : return NULL;
160 : }
161 :
162 3 : if (quote == PQUOTE_SQL_LITERAL)
163 2 : escaped_value =
164 2 : PQescapeLiteral(pset.db, value, strlen(value));
165 : else
166 1 : escaped_value =
167 1 : PQescapeIdentifier(pset.db, value, strlen(value));
168 :
169 3 : if (escaped_value == NULL)
170 : {
171 0 : const char *error = PQerrorMessage(pset.db);
172 :
173 0 : psql_error("%s", error);
174 0 : return NULL;
175 : }
176 :
177 : /*
178 : * Rather than complicate the lexer's API with a notion of
179 : * which free() routine to use, just pay the price of an extra
180 : * strdup().
181 : */
182 3 : result = pg_strdup(escaped_value);
183 3 : PQfreemem(escaped_value);
184 3 : break;
185 : }
186 : case PQUOTE_SHELL_ARG:
187 : {
188 : /*
189 : * For this we use appendShellStringNoError, which is
190 : * encoding-agnostic, which is fine since the shell probably
191 : * is too. In any case, the only special character is "'",
192 : * which is not known to appear in valid multibyte characters.
193 : */
194 : PQExpBufferData buf;
195 :
196 0 : initPQExpBuffer(&buf);
197 0 : if (!appendShellStringNoError(&buf, value))
198 : {
199 0 : psql_error("shell command argument contains a newline or carriage return: \"%s\"\n",
200 : value);
201 0 : free(buf.data);
202 0 : return NULL;
203 : }
204 0 : result = buf.data;
205 0 : break;
206 : }
207 :
208 : /* No default: we want a compiler warning for missing cases */
209 : }
210 :
211 47 : return result;
212 : }
213 :
214 :
215 : /*
216 : * Error reporting for scripts. Errors should look like
217 : * psql:filename:lineno: message
218 : */
219 : void
220 4987 : psql_error(const char *fmt,...)
221 : {
222 : va_list ap;
223 :
224 4987 : fflush(stdout);
225 4987 : if (pset.queryFout && pset.queryFout != stdout)
226 0 : fflush(pset.queryFout);
227 :
228 4987 : if (pset.inputfile)
229 0 : fprintf(stderr, "%s:%s:" UINT64_FORMAT ": ", pset.progname, pset.inputfile, pset.lineno);
230 4987 : va_start(ap, fmt);
231 4987 : vfprintf(stderr, _(fmt), ap);
232 4987 : va_end(ap);
233 4987 : }
234 :
235 :
236 :
237 : /*
238 : * for backend Notice messages (INFO, WARNING, etc)
239 : */
240 : void
241 1739 : NoticeProcessor(void *arg, const char *message)
242 : {
243 : (void) arg; /* not used */
244 1739 : psql_error("%s", message);
245 1739 : }
246 :
247 :
248 :
249 : /*
250 : * Code to support query cancellation
251 : *
252 : * Before we start a query, we enable the SIGINT signal catcher to send a
253 : * cancel request to the backend. Note that sending the cancel directly from
254 : * the signal handler is safe because PQcancel() is written to make it
255 : * so. We use write() to report to stderr because it's better to use simple
256 : * facilities in a signal handler.
257 : *
258 : * On win32, the signal canceling happens on a separate thread, because
259 : * that's how SetConsoleCtrlHandler works. The PQcancel function is safe
260 : * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
261 : * to protect the PGcancel structure against being changed while the signal
262 : * thread is using it.
263 : *
264 : * SIGINT is supposed to abort all long-running psql operations, not only
265 : * database queries. In most places, this is accomplished by checking
266 : * cancel_pressed during long-running loops. However, that won't work when
267 : * blocked on user input (in readline() or fgets()). In those places, we
268 : * set sigint_interrupt_enabled TRUE while blocked, instructing the signal
269 : * catcher to longjmp through sigint_interrupt_jmp. We assume readline and
270 : * fgets are coded to handle possible interruption. (XXX currently this does
271 : * not work on win32, so control-C is less useful there)
272 : */
273 : volatile bool sigint_interrupt_enabled = false;
274 :
275 : sigjmp_buf sigint_interrupt_jmp;
276 :
277 : static PGcancel *volatile cancelConn = NULL;
278 :
279 : #ifdef WIN32
280 : static CRITICAL_SECTION cancelConnLock;
281 : #endif
282 :
283 : /*
284 : * Write a simple string to stderr --- must be safe in a signal handler.
285 : * We ignore the write() result since there's not much we could do about it.
286 : * Certain compilers make that harder than it ought to be.
287 : */
288 : #define write_stderr(str) \
289 : do { \
290 : const char *str_ = (str); \
291 : int rc_; \
292 : rc_ = write(fileno(stderr), str_, strlen(str_)); \
293 : (void) rc_; \
294 : } while (0)
295 :
296 :
297 : #ifndef WIN32
298 :
299 : static void
300 0 : handle_sigint(SIGNAL_ARGS)
301 : {
302 0 : int save_errno = errno;
303 : char errbuf[256];
304 :
305 : /* if we are waiting for input, longjmp out of it */
306 0 : if (sigint_interrupt_enabled)
307 : {
308 0 : sigint_interrupt_enabled = false;
309 0 : siglongjmp(sigint_interrupt_jmp, 1);
310 : }
311 :
312 : /* else, set cancel flag to stop any long-running loops */
313 0 : cancel_pressed = true;
314 :
315 : /* and send QueryCancel if we are processing a database query */
316 0 : if (cancelConn != NULL)
317 : {
318 0 : if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
319 0 : write_stderr("Cancel request sent\n");
320 : else
321 : {
322 0 : write_stderr("Could not send cancel request: ");
323 0 : write_stderr(errbuf);
324 : }
325 : }
326 :
327 0 : errno = save_errno; /* just in case the write changed it */
328 0 : }
329 :
330 : void
331 182 : setup_cancel_handler(void)
332 : {
333 182 : pqsignal(SIGINT, handle_sigint);
334 182 : }
335 : #else /* WIN32 */
336 :
337 : static BOOL WINAPI
338 : consoleHandler(DWORD dwCtrlType)
339 : {
340 : char errbuf[256];
341 :
342 : if (dwCtrlType == CTRL_C_EVENT ||
343 : dwCtrlType == CTRL_BREAK_EVENT)
344 : {
345 : /*
346 : * Can't longjmp here, because we are in wrong thread :-(
347 : */
348 :
349 : /* set cancel flag to stop any long-running loops */
350 : cancel_pressed = true;
351 :
352 : /* and send QueryCancel if we are processing a database query */
353 : EnterCriticalSection(&cancelConnLock);
354 : if (cancelConn != NULL)
355 : {
356 : if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
357 : write_stderr("Cancel request sent\n");
358 : else
359 : {
360 : write_stderr("Could not send cancel request: ");
361 : write_stderr(errbuf);
362 : }
363 : }
364 : LeaveCriticalSection(&cancelConnLock);
365 :
366 : return TRUE;
367 : }
368 : else
369 : /* Return FALSE for any signals not being handled */
370 : return FALSE;
371 : }
372 :
373 : void
374 : setup_cancel_handler(void)
375 : {
376 : InitializeCriticalSection(&cancelConnLock);
377 :
378 : SetConsoleCtrlHandler(consoleHandler, TRUE);
379 : }
380 : #endif /* WIN32 */
381 :
382 :
383 : /* ConnectionUp
384 : *
385 : * Returns whether our backend connection is still there.
386 : */
387 : static bool
388 3207 : ConnectionUp(void)
389 : {
390 3207 : return PQstatus(pset.db) != CONNECTION_BAD;
391 : }
392 :
393 :
394 :
395 : /* CheckConnection
396 : *
397 : * Verify that we still have a good connection to the backend, and if not,
398 : * see if it can be restored.
399 : *
400 : * Returns true if either the connection was still there, or it could be
401 : * restored successfully; false otherwise. If, however, there was no
402 : * connection and the session is non-interactive, this will exit the program
403 : * with a code of EXIT_BADCONN.
404 : */
405 : static bool
406 3207 : CheckConnection(void)
407 : {
408 : bool OK;
409 :
410 3207 : OK = ConnectionUp();
411 3207 : if (!OK)
412 : {
413 0 : if (!pset.cur_cmd_interactive)
414 : {
415 0 : psql_error("connection to server was lost\n");
416 0 : exit(EXIT_BADCONN);
417 : }
418 :
419 0 : psql_error("The connection to the server was lost. Attempting reset: ");
420 0 : PQreset(pset.db);
421 0 : OK = ConnectionUp();
422 0 : if (!OK)
423 : {
424 0 : psql_error("Failed.\n");
425 0 : PQfinish(pset.db);
426 0 : pset.db = NULL;
427 0 : ResetCancelConn();
428 0 : UnsyncVariables();
429 : }
430 : else
431 0 : psql_error("Succeeded.\n");
432 : }
433 :
434 3207 : return OK;
435 : }
436 :
437 :
438 :
439 : /*
440 : * SetCancelConn
441 : *
442 : * Set cancelConn to point to the current database connection.
443 : */
444 : void
445 26564 : SetCancelConn(void)
446 : {
447 : PGcancel *oldCancelConn;
448 :
449 : #ifdef WIN32
450 : EnterCriticalSection(&cancelConnLock);
451 : #endif
452 :
453 : /* Free the old one if we have one */
454 26564 : oldCancelConn = cancelConn;
455 : /* be sure handle_sigint doesn't use pointer while freeing */
456 26564 : cancelConn = NULL;
457 :
458 26564 : if (oldCancelConn != NULL)
459 0 : PQfreeCancel(oldCancelConn);
460 :
461 26564 : cancelConn = PQgetCancel(pset.db);
462 :
463 : #ifdef WIN32
464 : LeaveCriticalSection(&cancelConnLock);
465 : #endif
466 26564 : }
467 :
468 :
469 : /*
470 : * ResetCancelConn
471 : *
472 : * Free the current cancel connection, if any, and set to NULL.
473 : */
474 : void
475 26564 : ResetCancelConn(void)
476 : {
477 : PGcancel *oldCancelConn;
478 :
479 : #ifdef WIN32
480 : EnterCriticalSection(&cancelConnLock);
481 : #endif
482 :
483 26564 : oldCancelConn = cancelConn;
484 : /* be sure handle_sigint doesn't use pointer while freeing */
485 26564 : cancelConn = NULL;
486 :
487 26564 : if (oldCancelConn != NULL)
488 26564 : PQfreeCancel(oldCancelConn);
489 :
490 : #ifdef WIN32
491 : LeaveCriticalSection(&cancelConnLock);
492 : #endif
493 26564 : }
494 :
495 :
496 : /*
497 : * AcceptResult
498 : *
499 : * Checks whether a result is valid, giving an error message if necessary;
500 : * and ensures that the connection to the backend is still up.
501 : *
502 : * Returns true for valid result, false for error state.
503 : */
504 : static bool
505 26454 : AcceptResult(const PGresult *result)
506 : {
507 : bool OK;
508 :
509 26454 : if (!result)
510 0 : OK = false;
511 : else
512 26454 : switch (PQresultStatus(result))
513 : {
514 : case PGRES_COMMAND_OK:
515 : case PGRES_TUPLES_OK:
516 : case PGRES_EMPTY_QUERY:
517 : case PGRES_COPY_IN:
518 : case PGRES_COPY_OUT:
519 : /* Fine, do nothing */
520 23250 : OK = true;
521 23250 : break;
522 :
523 : case PGRES_BAD_RESPONSE:
524 : case PGRES_NONFATAL_ERROR:
525 : case PGRES_FATAL_ERROR:
526 3204 : OK = false;
527 3204 : break;
528 :
529 : default:
530 0 : OK = false;
531 0 : psql_error("unexpected PQresultStatus: %d\n",
532 0 : PQresultStatus(result));
533 0 : break;
534 : }
535 :
536 26454 : if (!OK)
537 : {
538 3204 : const char *error = PQerrorMessage(pset.db);
539 :
540 3204 : if (strlen(error))
541 3204 : psql_error("%s", error);
542 :
543 3204 : CheckConnection();
544 : }
545 :
546 26454 : return OK;
547 : }
548 :
549 :
550 : /*
551 : * ClearOrSaveResult
552 : *
553 : * If the result represents an error, remember it for possible display by
554 : * \errverbose. Otherwise, just PQclear() it.
555 : */
556 : static void
557 24330 : ClearOrSaveResult(PGresult *result)
558 : {
559 24330 : if (result)
560 : {
561 24258 : switch (PQresultStatus(result))
562 : {
563 : case PGRES_NONFATAL_ERROR:
564 : case PGRES_FATAL_ERROR:
565 3220 : if (pset.last_error_result)
566 3080 : PQclear(pset.last_error_result);
567 3220 : pset.last_error_result = result;
568 3220 : break;
569 :
570 : default:
571 21038 : PQclear(result);
572 21038 : break;
573 : }
574 : }
575 24330 : }
576 :
577 :
578 : /*
579 : * Print microtiming output. Always print raw milliseconds; if the interval
580 : * is >= 1 second, also break it down into days/hours/minutes/seconds.
581 : */
582 : static void
583 0 : PrintTiming(double elapsed_msec)
584 : {
585 : double seconds;
586 : double minutes;
587 : double hours;
588 : double days;
589 :
590 0 : if (elapsed_msec < 1000.0)
591 : {
592 : /* This is the traditional (pre-v10) output format */
593 0 : printf(_("Time: %.3f ms\n"), elapsed_msec);
594 0 : return;
595 : }
596 :
597 : /*
598 : * Note: we could print just seconds, in a format like %06.3f, when the
599 : * total is less than 1min. But that's hard to interpret unless we tack
600 : * on "s" or otherwise annotate it. Forcing the display to include
601 : * minutes seems like a better solution.
602 : */
603 0 : seconds = elapsed_msec / 1000.0;
604 0 : minutes = floor(seconds / 60.0);
605 0 : seconds -= 60.0 * minutes;
606 0 : if (minutes < 60.0)
607 : {
608 0 : printf(_("Time: %.3f ms (%02d:%06.3f)\n"),
609 : elapsed_msec, (int) minutes, seconds);
610 0 : return;
611 : }
612 :
613 0 : hours = floor(minutes / 60.0);
614 0 : minutes -= 60.0 * hours;
615 0 : if (hours < 24.0)
616 : {
617 0 : printf(_("Time: %.3f ms (%02d:%02d:%06.3f)\n"),
618 : elapsed_msec, (int) hours, (int) minutes, seconds);
619 0 : return;
620 : }
621 :
622 0 : days = floor(hours / 24.0);
623 0 : hours -= 24.0 * days;
624 0 : printf(_("Time: %.3f ms (%.0f d %02d:%02d:%06.3f)\n"),
625 : elapsed_msec, days, (int) hours, (int) minutes, seconds);
626 : }
627 :
628 :
629 : /*
630 : * PSQLexec
631 : *
632 : * This is the way to send "backdoor" queries (those not directly entered
633 : * by the user). It is subject to -E but not -e.
634 : *
635 : * Caller is responsible for handling the ensuing processing if a COPY
636 : * command is sent.
637 : *
638 : * Note: we don't bother to check PQclientEncoding; it is assumed that no
639 : * caller uses this path to issue "SET CLIENT_ENCODING".
640 : */
641 : PGresult *
642 2150 : PSQLexec(const char *query)
643 : {
644 : PGresult *res;
645 :
646 2150 : if (!pset.db)
647 : {
648 0 : psql_error("You are currently not connected to a database.\n");
649 0 : return NULL;
650 : }
651 :
652 2150 : if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF)
653 : {
654 0 : printf(_("********* QUERY **********\n"
655 : "%s\n"
656 : "**************************\n\n"), query);
657 0 : fflush(stdout);
658 0 : if (pset.logfile)
659 : {
660 0 : fprintf(pset.logfile,
661 : _("********* QUERY **********\n"
662 : "%s\n"
663 : "**************************\n\n"), query);
664 0 : fflush(pset.logfile);
665 : }
666 :
667 0 : if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC)
668 0 : return NULL;
669 : }
670 :
671 2150 : SetCancelConn();
672 :
673 2150 : res = PQexec(pset.db, query);
674 :
675 2150 : ResetCancelConn();
676 :
677 2150 : if (!AcceptResult(res))
678 : {
679 0 : ClearOrSaveResult(res);
680 0 : res = NULL;
681 : }
682 :
683 2150 : return res;
684 : }
685 :
686 :
687 : /*
688 : * PSQLexecWatch
689 : *
690 : * This function is used for \watch command to send the query to
691 : * the server and print out the results.
692 : *
693 : * Returns 1 if the query executed successfully, 0 if it cannot be repeated,
694 : * e.g., because of the interrupt, -1 on error.
695 : */
696 : int
697 0 : PSQLexecWatch(const char *query, const printQueryOpt *opt)
698 : {
699 : PGresult *res;
700 0 : double elapsed_msec = 0;
701 : instr_time before;
702 : instr_time after;
703 :
704 0 : if (!pset.db)
705 : {
706 0 : psql_error("You are currently not connected to a database.\n");
707 0 : return 0;
708 : }
709 :
710 0 : SetCancelConn();
711 :
712 0 : if (pset.timing)
713 0 : INSTR_TIME_SET_CURRENT(before);
714 :
715 0 : res = PQexec(pset.db, query);
716 :
717 0 : ResetCancelConn();
718 :
719 0 : if (!AcceptResult(res))
720 : {
721 0 : ClearOrSaveResult(res);
722 0 : return 0;
723 : }
724 :
725 0 : if (pset.timing)
726 : {
727 0 : INSTR_TIME_SET_CURRENT(after);
728 0 : INSTR_TIME_SUBTRACT(after, before);
729 0 : elapsed_msec = INSTR_TIME_GET_MILLISEC(after);
730 : }
731 :
732 : /*
733 : * If SIGINT is sent while the query is processing, the interrupt will be
734 : * consumed. The user's intention, though, is to cancel the entire watch
735 : * process, so detect a sent cancellation request and exit in this case.
736 : */
737 0 : if (cancel_pressed)
738 : {
739 0 : PQclear(res);
740 0 : return 0;
741 : }
742 :
743 0 : switch (PQresultStatus(res))
744 : {
745 : case PGRES_TUPLES_OK:
746 0 : printQuery(res, opt, pset.queryFout, false, pset.logfile);
747 0 : break;
748 :
749 : case PGRES_COMMAND_OK:
750 0 : fprintf(pset.queryFout, "%s\n%s\n\n", opt->title, PQcmdStatus(res));
751 0 : break;
752 :
753 : case PGRES_EMPTY_QUERY:
754 0 : psql_error(_("\\watch cannot be used with an empty query\n"));
755 0 : PQclear(res);
756 0 : return -1;
757 :
758 : case PGRES_COPY_OUT:
759 : case PGRES_COPY_IN:
760 : case PGRES_COPY_BOTH:
761 0 : psql_error(_("\\watch cannot be used with COPY\n"));
762 0 : PQclear(res);
763 0 : return -1;
764 :
765 : default:
766 0 : psql_error(_("unexpected result status for \\watch\n"));
767 0 : PQclear(res);
768 0 : return -1;
769 : }
770 :
771 0 : PQclear(res);
772 :
773 0 : fflush(pset.queryFout);
774 :
775 : /* Possible microtiming output */
776 0 : if (pset.timing)
777 0 : PrintTiming(elapsed_msec);
778 :
779 0 : return 1;
780 : }
781 :
782 :
783 : /*
784 : * PrintNotifications: check for asynchronous notifications, and print them out
785 : */
786 : static void
787 24267 : PrintNotifications(void)
788 : {
789 : PGnotify *notify;
790 :
791 48534 : while ((notify = PQnotifies(pset.db)))
792 : {
793 : /* for backward compatibility, only show payload if nonempty */
794 0 : if (notify->extra[0])
795 0 : fprintf(pset.queryFout, _("Asynchronous notification \"%s\" with payload \"%s\" received from server process with PID %d.\n"),
796 : notify->relname, notify->extra, notify->be_pid);
797 : else
798 0 : fprintf(pset.queryFout, _("Asynchronous notification \"%s\" received from server process with PID %d.\n"),
799 : notify->relname, notify->be_pid);
800 0 : fflush(pset.queryFout);
801 0 : PQfreemem(notify);
802 : }
803 24267 : }
804 :
805 :
806 : /*
807 : * PrintQueryTuples: assuming query result is OK, print its tuples
808 : *
809 : * Returns true if successful, false otherwise.
810 : */
811 : static bool
812 8995 : PrintQueryTuples(const PGresult *results)
813 : {
814 8995 : printQueryOpt my_popt = pset.popt;
815 :
816 : /* one-shot expanded output requested via \gx */
817 8995 : if (pset.g_expanded)
818 2 : my_popt.topt.expanded = 1;
819 :
820 : /* write output to \g argument, if any */
821 8995 : if (pset.gfname)
822 : {
823 : FILE *fout;
824 : bool is_pipe;
825 :
826 0 : if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe))
827 0 : return false;
828 0 : if (is_pipe)
829 0 : disable_sigpipe_trap();
830 :
831 0 : printQuery(results, &my_popt, fout, false, pset.logfile);
832 :
833 0 : if (is_pipe)
834 : {
835 0 : pclose(fout);
836 0 : restore_sigpipe_trap();
837 : }
838 : else
839 0 : fclose(fout);
840 : }
841 : else
842 8995 : printQuery(results, &my_popt, pset.queryFout, false, pset.logfile);
843 :
844 8995 : return true;
845 : }
846 :
847 :
848 : /*
849 : * StoreQueryTuple: assuming query result is OK, save data into variables
850 : *
851 : * Returns true if successful, false otherwise.
852 : */
853 : static bool
854 20 : StoreQueryTuple(const PGresult *result)
855 : {
856 20 : bool success = true;
857 :
858 20 : if (PQntuples(result) < 1)
859 : {
860 2 : psql_error("no rows returned for \\gset\n");
861 2 : success = false;
862 : }
863 18 : else if (PQntuples(result) > 1)
864 : {
865 2 : psql_error("more than one row returned for \\gset\n");
866 2 : success = false;
867 : }
868 : else
869 : {
870 : int i;
871 :
872 41 : for (i = 0; i < PQnfields(result); i++)
873 : {
874 26 : char *colname = PQfname(result, i);
875 : char *varname;
876 : char *value;
877 :
878 : /* concatenate prefix and column name */
879 26 : varname = psprintf("%s%s", pset.gset_prefix, colname);
880 :
881 26 : if (!PQgetisnull(result, 0, i))
882 25 : value = PQgetvalue(result, 0, i);
883 : else
884 : {
885 : /* for NULL value, unset rather than set the variable */
886 1 : value = NULL;
887 : }
888 :
889 26 : if (!SetVariable(pset.vars, varname, value))
890 : {
891 1 : free(varname);
892 1 : success = false;
893 1 : break;
894 : }
895 :
896 25 : free(varname);
897 : }
898 : }
899 :
900 20 : return success;
901 : }
902 :
903 :
904 : /*
905 : * ExecQueryTuples: assuming query result is OK, execute each query
906 : * result field as a SQL statement
907 : *
908 : * Returns true if successful, false otherwise.
909 : */
910 : static bool
911 2 : ExecQueryTuples(const PGresult *result)
912 : {
913 2 : bool success = true;
914 2 : int nrows = PQntuples(result);
915 2 : int ncolumns = PQnfields(result);
916 : int r,
917 : c;
918 :
919 : /*
920 : * We must turn off gexec_flag to avoid infinite recursion. Note that
921 : * this allows ExecQueryUsingCursor to be applied to the individual query
922 : * results. SendQuery prevents it from being applied when fetching the
923 : * queries-to-execute, because it can't handle recursion either.
924 : */
925 2 : pset.gexec_flag = false;
926 :
927 9 : for (r = 0; r < nrows; r++)
928 : {
929 17 : for (c = 0; c < ncolumns; c++)
930 : {
931 10 : if (!PQgetisnull(result, r, c))
932 : {
933 9 : const char *query = PQgetvalue(result, r, c);
934 :
935 : /* Abandon execution if cancel_pressed */
936 9 : if (cancel_pressed)
937 0 : goto loop_exit;
938 :
939 : /*
940 : * ECHO_ALL mode should echo these queries, but SendQuery
941 : * assumes that MainLoop did that, so we have to do it here.
942 : */
943 9 : if (pset.echo == PSQL_ECHO_ALL && !pset.singlestep)
944 : {
945 9 : puts(query);
946 9 : fflush(stdout);
947 : }
948 :
949 9 : if (!SendQuery(query))
950 : {
951 : /* Error - abandon execution if ON_ERROR_STOP */
952 1 : success = false;
953 1 : if (pset.on_error_stop)
954 0 : goto loop_exit;
955 : }
956 : }
957 : }
958 : }
959 :
960 : loop_exit:
961 :
962 : /*
963 : * Restore state. We know gexec_flag was on, else we'd not be here. (We
964 : * also know it'll get turned off at end of command, but that's not ours
965 : * to do here.)
966 : */
967 2 : pset.gexec_flag = true;
968 :
969 : /* Return true if all queries were successful */
970 2 : return success;
971 : }
972 :
973 :
974 : /*
975 : * ProcessResult: utility function for use by SendQuery() only
976 : *
977 : * When our command string contained a COPY FROM STDIN or COPY TO STDOUT,
978 : * PQexec() has stopped at the PGresult associated with the first such
979 : * command. In that event, we'll marshal data for the COPY and then cycle
980 : * through any subsequent PGresult objects.
981 : *
982 : * When the command string contained no such COPY command, this function
983 : * degenerates to an AcceptResult() call.
984 : *
985 : * Changes its argument to point to the last PGresult of the command string,
986 : * or NULL if that result was for a COPY TO STDOUT. (Returning NULL prevents
987 : * the command status from being printed, which we want in that case so that
988 : * the status line doesn't get taken as part of the COPY data.)
989 : *
990 : * Returns true on complete success, false otherwise. Possible failure modes
991 : * include purely client-side problems; check the transaction status for the
992 : * server-side opinion.
993 : */
994 : static bool
995 24256 : ProcessResult(PGresult **results)
996 : {
997 24256 : bool success = true;
998 24256 : bool first_cycle = true;
999 :
1000 : for (;;)
1001 : {
1002 : ExecStatusType result_status;
1003 : bool is_copy;
1004 : PGresult *next_result;
1005 :
1006 24262 : if (!AcceptResult(*results))
1007 : {
1008 : /*
1009 : * Failure at this point is always a server-side failure or a
1010 : * failure to submit the command string. Either way, we're
1011 : * finished with this command string.
1012 : */
1013 3204 : success = false;
1014 3204 : break;
1015 : }
1016 :
1017 21058 : result_status = PQresultStatus(*results);
1018 21058 : switch (result_status)
1019 : {
1020 : case PGRES_EMPTY_QUERY:
1021 : case PGRES_COMMAND_OK:
1022 : case PGRES_TUPLES_OK:
1023 20917 : is_copy = false;
1024 20917 : break;
1025 :
1026 : case PGRES_COPY_OUT:
1027 : case PGRES_COPY_IN:
1028 141 : is_copy = true;
1029 141 : break;
1030 :
1031 : default:
1032 : /* AcceptResult() should have caught anything else. */
1033 0 : is_copy = false;
1034 0 : psql_error("unexpected PQresultStatus: %d\n", result_status);
1035 0 : break;
1036 : }
1037 :
1038 21058 : if (is_copy)
1039 : {
1040 : /*
1041 : * Marshal the COPY data. Either subroutine will get the
1042 : * connection out of its COPY state, then call PQresultStatus()
1043 : * once and report any error.
1044 : *
1045 : * If pset.copyStream is set, use that as data source/sink,
1046 : * otherwise use queryFout or cur_cmd_source as appropriate.
1047 : */
1048 141 : FILE *copystream = pset.copyStream;
1049 : PGresult *copy_result;
1050 :
1051 141 : SetCancelConn();
1052 141 : if (result_status == PGRES_COPY_OUT)
1053 : {
1054 64 : if (!copystream)
1055 55 : copystream = pset.queryFout;
1056 128 : success = handleCopyOut(pset.db,
1057 : copystream,
1058 64 : ©_result) && success;
1059 :
1060 : /*
1061 : * Suppress status printing if the report would go to the same
1062 : * place as the COPY data just went. Note this doesn't
1063 : * prevent error reporting, since handleCopyOut did that.
1064 : */
1065 64 : if (copystream == pset.queryFout)
1066 : {
1067 64 : PQclear(copy_result);
1068 64 : copy_result = NULL;
1069 : }
1070 : }
1071 : else
1072 : {
1073 77 : if (!copystream)
1074 76 : copystream = pset.cur_cmd_source;
1075 154 : success = handleCopyIn(pset.db,
1076 : copystream,
1077 77 : PQbinaryTuples(*results),
1078 77 : ©_result) && success;
1079 : }
1080 141 : ResetCancelConn();
1081 :
1082 : /*
1083 : * Replace the PGRES_COPY_OUT/IN result with COPY command's exit
1084 : * status, or with NULL if we want to suppress printing anything.
1085 : */
1086 141 : PQclear(*results);
1087 141 : *results = copy_result;
1088 : }
1089 20917 : else if (first_cycle)
1090 : {
1091 : /* fast path: no COPY commands; PQexec visited all results */
1092 20914 : break;
1093 : }
1094 :
1095 : /*
1096 : * Check PQgetResult() again. In the typical case of a single-command
1097 : * string, it will return NULL. Otherwise, we'll have other results
1098 : * to process that may include other COPYs. We keep the last result.
1099 : */
1100 144 : next_result = PQgetResult(pset.db);
1101 144 : if (!next_result)
1102 138 : break;
1103 :
1104 6 : PQclear(*results);
1105 6 : *results = next_result;
1106 6 : first_cycle = false;
1107 6 : }
1108 :
1109 : /* may need this to recover from conn loss during COPY */
1110 24256 : if (!first_cycle && !CheckConnection())
1111 0 : return false;
1112 :
1113 24256 : return success;
1114 : }
1115 :
1116 :
1117 : /*
1118 : * PrintQueryStatus: report command status as required
1119 : *
1120 : * Note: Utility function for use by PrintQueryResults() only.
1121 : */
1122 : static void
1123 12065 : PrintQueryStatus(PGresult *results)
1124 : {
1125 : char buf[16];
1126 :
1127 12065 : if (!pset.quiet)
1128 : {
1129 41 : if (pset.popt.topt.format == PRINT_HTML)
1130 : {
1131 0 : fputs("<p>", pset.queryFout);
1132 0 : html_escaped_print(PQcmdStatus(results), pset.queryFout);
1133 0 : fputs("</p>\n", pset.queryFout);
1134 : }
1135 : else
1136 41 : fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
1137 : }
1138 :
1139 12065 : if (pset.logfile)
1140 0 : fprintf(pset.logfile, "%s\n", PQcmdStatus(results));
1141 :
1142 12065 : snprintf(buf, sizeof(buf), "%u", (unsigned int) PQoidValue(results));
1143 12065 : SetVariable(pset.vars, "LASTOID", buf);
1144 12065 : }
1145 :
1146 :
1147 : /*
1148 : * PrintQueryResults: print out (or store or execute) query results as required
1149 : *
1150 : * Note: Utility function for use by SendQuery() only.
1151 : *
1152 : * Returns true if the query executed successfully, false otherwise.
1153 : */
1154 : static bool
1155 20975 : PrintQueryResults(PGresult *results)
1156 : {
1157 : bool success;
1158 : const char *cmdstatus;
1159 :
1160 20975 : if (!results)
1161 0 : return false;
1162 :
1163 20975 : switch (PQresultStatus(results))
1164 : {
1165 : case PGRES_TUPLES_OK:
1166 : /* store or execute or print the data ... */
1167 9035 : if (pset.gset_prefix)
1168 16 : success = StoreQueryTuple(results);
1169 9019 : else if (pset.gexec_flag)
1170 2 : success = ExecQueryTuples(results);
1171 9017 : else if (pset.crosstab_flag)
1172 22 : success = PrintResultsInCrosstab(results);
1173 : else
1174 8995 : success = PrintQueryTuples(results);
1175 : /* if it's INSERT/UPDATE/DELETE RETURNING, also print status */
1176 9035 : cmdstatus = PQcmdStatus(results);
1177 18007 : if (strncmp(cmdstatus, "INSERT", 6) == 0 ||
1178 17896 : strncmp(cmdstatus, "UPDATE", 6) == 0 ||
1179 8924 : strncmp(cmdstatus, "DELETE", 6) == 0)
1180 130 : PrintQueryStatus(results);
1181 9035 : break;
1182 :
1183 : case PGRES_COMMAND_OK:
1184 11935 : PrintQueryStatus(results);
1185 11935 : success = true;
1186 11935 : break;
1187 :
1188 : case PGRES_EMPTY_QUERY:
1189 5 : success = true;
1190 5 : break;
1191 :
1192 : case PGRES_COPY_OUT:
1193 : case PGRES_COPY_IN:
1194 : /* nothing to do here */
1195 0 : success = true;
1196 0 : break;
1197 :
1198 : case PGRES_BAD_RESPONSE:
1199 : case PGRES_NONFATAL_ERROR:
1200 : case PGRES_FATAL_ERROR:
1201 0 : success = false;
1202 0 : break;
1203 :
1204 : default:
1205 0 : success = false;
1206 0 : psql_error("unexpected PQresultStatus: %d\n",
1207 0 : PQresultStatus(results));
1208 0 : break;
1209 : }
1210 :
1211 20975 : fflush(pset.queryFout);
1212 :
1213 20975 : return success;
1214 : }
1215 :
1216 :
1217 : /*
1218 : * SendQuery: send the query string to the backend
1219 : * (and print out results)
1220 : *
1221 : * Note: This is the "front door" way to send a query. That is, use it to
1222 : * send queries actually entered by the user. These queries will be subject to
1223 : * single step mode.
1224 : * To send "back door" queries (generated by slash commands, etc.) in a
1225 : * controlled way, use PSQLexec().
1226 : *
1227 : * Returns true if the query executed successfully, false otherwise.
1228 : */
1229 : bool
1230 24267 : SendQuery(const char *query)
1231 : {
1232 : PGresult *results;
1233 : PGTransactionStatusType transaction_status;
1234 24267 : double elapsed_msec = 0;
1235 24267 : bool OK = false;
1236 : int i;
1237 24267 : bool on_error_rollback_savepoint = false;
1238 : static bool on_error_rollback_warning = false;
1239 :
1240 24267 : if (!pset.db)
1241 : {
1242 0 : psql_error("You are currently not connected to a database.\n");
1243 0 : goto sendquery_cleanup;
1244 : }
1245 :
1246 24267 : if (pset.singlestep)
1247 : {
1248 : char buf[3];
1249 :
1250 0 : fflush(stderr);
1251 0 : printf(_("***(Single step mode: verify command)*******************************************\n"
1252 : "%s\n"
1253 : "***(press return to proceed or enter x and return to cancel)********************\n"),
1254 : query);
1255 0 : fflush(stdout);
1256 0 : if (fgets(buf, sizeof(buf), stdin) != NULL)
1257 0 : if (buf[0] == 'x')
1258 0 : goto sendquery_cleanup;
1259 0 : if (cancel_pressed)
1260 0 : goto sendquery_cleanup;
1261 : }
1262 24267 : else if (pset.echo == PSQL_ECHO_QUERIES)
1263 : {
1264 0 : puts(query);
1265 0 : fflush(stdout);
1266 : }
1267 :
1268 24267 : if (pset.logfile)
1269 : {
1270 0 : fprintf(pset.logfile,
1271 : _("********* QUERY **********\n"
1272 : "%s\n"
1273 : "**************************\n\n"), query);
1274 0 : fflush(pset.logfile);
1275 : }
1276 :
1277 24267 : SetCancelConn();
1278 :
1279 24267 : transaction_status = PQtransactionStatus(pset.db);
1280 :
1281 46260 : if (transaction_status == PQTRANS_IDLE &&
1282 21993 : !pset.autocommit &&
1283 0 : !command_no_begin(query))
1284 : {
1285 0 : results = PQexec(pset.db, "BEGIN");
1286 0 : if (PQresultStatus(results) != PGRES_COMMAND_OK)
1287 : {
1288 0 : psql_error("%s", PQerrorMessage(pset.db));
1289 0 : ClearOrSaveResult(results);
1290 0 : ResetCancelConn();
1291 0 : goto sendquery_cleanup;
1292 : }
1293 0 : ClearOrSaveResult(results);
1294 0 : transaction_status = PQtransactionStatus(pset.db);
1295 : }
1296 :
1297 26428 : if (transaction_status == PQTRANS_INTRANS &&
1298 2161 : pset.on_error_rollback != PSQL_ERROR_ROLLBACK_OFF &&
1299 0 : (pset.cur_cmd_interactive ||
1300 0 : pset.on_error_rollback == PSQL_ERROR_ROLLBACK_ON))
1301 : {
1302 0 : if (on_error_rollback_warning == false && pset.sversion < 80000)
1303 0 : {
1304 : char sverbuf[32];
1305 :
1306 0 : psql_error("The server (version %s) does not support savepoints for ON_ERROR_ROLLBACK.\n",
1307 : formatPGVersionNumber(pset.sversion, false,
1308 : sverbuf, sizeof(sverbuf)));
1309 0 : on_error_rollback_warning = true;
1310 : }
1311 : else
1312 : {
1313 0 : results = PQexec(pset.db, "SAVEPOINT pg_psql_temporary_savepoint");
1314 0 : if (PQresultStatus(results) != PGRES_COMMAND_OK)
1315 : {
1316 0 : psql_error("%s", PQerrorMessage(pset.db));
1317 0 : ClearOrSaveResult(results);
1318 0 : ResetCancelConn();
1319 0 : goto sendquery_cleanup;
1320 : }
1321 0 : ClearOrSaveResult(results);
1322 0 : on_error_rollback_savepoint = true;
1323 : }
1324 : }
1325 :
1326 24280 : if (pset.fetch_count <= 0 || pset.gexec_flag ||
1327 26 : pset.crosstab_flag || !is_select_command(query))
1328 24256 : {
1329 : /* Default fetch-it-all-and-print mode */
1330 : instr_time before,
1331 : after;
1332 :
1333 24256 : if (pset.timing)
1334 0 : INSTR_TIME_SET_CURRENT(before);
1335 :
1336 24256 : results = PQexec(pset.db, query);
1337 :
1338 : /* these operations are included in the timing result: */
1339 24256 : ResetCancelConn();
1340 24256 : OK = ProcessResult(&results);
1341 :
1342 24256 : if (pset.timing)
1343 : {
1344 0 : INSTR_TIME_SET_CURRENT(after);
1345 0 : INSTR_TIME_SUBTRACT(after, before);
1346 0 : elapsed_msec = INSTR_TIME_GET_MILLISEC(after);
1347 : }
1348 :
1349 : /* but printing results isn't: */
1350 24256 : if (OK && results)
1351 20975 : OK = PrintQueryResults(results);
1352 : }
1353 : else
1354 : {
1355 : /* Fetch-in-segments mode */
1356 11 : OK = ExecQueryUsingCursor(query, &elapsed_msec);
1357 11 : ResetCancelConn();
1358 11 : results = NULL; /* PQclear(NULL) does nothing */
1359 : }
1360 :
1361 24267 : if (!OK && pset.echo == PSQL_ECHO_ERRORS)
1362 0 : psql_error("STATEMENT: %s\n", query);
1363 :
1364 : /* If we made a temporary savepoint, possibly release/rollback */
1365 24267 : if (on_error_rollback_savepoint)
1366 : {
1367 0 : const char *svptcmd = NULL;
1368 :
1369 0 : transaction_status = PQtransactionStatus(pset.db);
1370 :
1371 0 : switch (transaction_status)
1372 : {
1373 : case PQTRANS_INERROR:
1374 : /* We always rollback on an error */
1375 0 : svptcmd = "ROLLBACK TO pg_psql_temporary_savepoint";
1376 0 : break;
1377 :
1378 : case PQTRANS_IDLE:
1379 : /* If they are no longer in a transaction, then do nothing */
1380 0 : break;
1381 :
1382 : case PQTRANS_INTRANS:
1383 :
1384 : /*
1385 : * Do nothing if they are messing with savepoints themselves:
1386 : * If the user did RELEASE or ROLLBACK, our savepoint is gone.
1387 : * If they issued a SAVEPOINT, releasing ours would remove
1388 : * theirs.
1389 : */
1390 0 : if (results &&
1391 0 : (strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 ||
1392 0 : strcmp(PQcmdStatus(results), "RELEASE") == 0 ||
1393 0 : strcmp(PQcmdStatus(results), "ROLLBACK") == 0))
1394 0 : svptcmd = NULL;
1395 : else
1396 0 : svptcmd = "RELEASE pg_psql_temporary_savepoint";
1397 0 : break;
1398 :
1399 : case PQTRANS_ACTIVE:
1400 : case PQTRANS_UNKNOWN:
1401 : default:
1402 0 : OK = false;
1403 : /* PQTRANS_UNKNOWN is expected given a broken connection. */
1404 0 : if (transaction_status != PQTRANS_UNKNOWN || ConnectionUp())
1405 0 : psql_error("unexpected transaction status (%d)\n",
1406 : transaction_status);
1407 0 : break;
1408 : }
1409 :
1410 0 : if (svptcmd)
1411 : {
1412 : PGresult *svptres;
1413 :
1414 0 : svptres = PQexec(pset.db, svptcmd);
1415 0 : if (PQresultStatus(svptres) != PGRES_COMMAND_OK)
1416 : {
1417 0 : psql_error("%s", PQerrorMessage(pset.db));
1418 0 : ClearOrSaveResult(svptres);
1419 0 : OK = false;
1420 :
1421 0 : PQclear(results);
1422 0 : ResetCancelConn();
1423 0 : goto sendquery_cleanup;
1424 : }
1425 0 : PQclear(svptres);
1426 : }
1427 : }
1428 :
1429 24267 : ClearOrSaveResult(results);
1430 :
1431 : /* Possible microtiming output */
1432 24267 : if (pset.timing)
1433 0 : PrintTiming(elapsed_msec);
1434 :
1435 : /* check for events that may occur during query execution */
1436 :
1437 24267 : if (pset.encoding != PQclientEncoding(pset.db) &&
1438 0 : PQclientEncoding(pset.db) >= 0)
1439 : {
1440 : /* track effects of SET CLIENT_ENCODING */
1441 0 : pset.encoding = PQclientEncoding(pset.db);
1442 0 : pset.popt.topt.encoding = pset.encoding;
1443 0 : SetVariable(pset.vars, "ENCODING",
1444 : pg_encoding_to_char(pset.encoding));
1445 : }
1446 :
1447 24267 : PrintNotifications();
1448 :
1449 : /* perform cleanup that should occur after any attempted query */
1450 :
1451 : sendquery_cleanup:
1452 :
1453 : /* reset \g's output-to-filename trigger */
1454 24267 : if (pset.gfname)
1455 : {
1456 0 : free(pset.gfname);
1457 0 : pset.gfname = NULL;
1458 : }
1459 :
1460 : /* reset \gx's expanded-mode flag */
1461 24267 : pset.g_expanded = false;
1462 :
1463 : /* reset \gset trigger */
1464 24267 : if (pset.gset_prefix)
1465 : {
1466 20 : free(pset.gset_prefix);
1467 20 : pset.gset_prefix = NULL;
1468 : }
1469 :
1470 : /* reset \gexec trigger */
1471 24267 : pset.gexec_flag = false;
1472 :
1473 : /* reset \crosstabview trigger */
1474 24267 : pset.crosstab_flag = false;
1475 121335 : for (i = 0; i < lengthof(pset.ctv_args); i++)
1476 : {
1477 97068 : pg_free(pset.ctv_args[i]);
1478 97068 : pset.ctv_args[i] = NULL;
1479 : }
1480 :
1481 24267 : return OK;
1482 : }
1483 :
1484 :
1485 : /*
1486 : * ExecQueryUsingCursor: run a SELECT-like query using a cursor
1487 : *
1488 : * This feature allows result sets larger than RAM to be dealt with.
1489 : *
1490 : * Returns true if the query executed successfully, false otherwise.
1491 : *
1492 : * If pset.timing is on, total query time (exclusive of result-printing) is
1493 : * stored into *elapsed_msec.
1494 : */
1495 : static bool
1496 11 : ExecQueryUsingCursor(const char *query, double *elapsed_msec)
1497 : {
1498 11 : bool OK = true;
1499 : PGresult *results;
1500 : PQExpBufferData buf;
1501 11 : printQueryOpt my_popt = pset.popt;
1502 : FILE *fout;
1503 : bool is_pipe;
1504 11 : bool is_pager = false;
1505 11 : bool started_txn = false;
1506 : int ntuples;
1507 : int fetch_count;
1508 : char fetch_cmd[64];
1509 : instr_time before,
1510 : after;
1511 : int flush_error;
1512 :
1513 11 : *elapsed_msec = 0;
1514 :
1515 : /* initialize print options for partial table output */
1516 11 : my_popt.topt.start_table = true;
1517 11 : my_popt.topt.stop_table = false;
1518 11 : my_popt.topt.prior_records = 0;
1519 :
1520 11 : if (pset.timing)
1521 0 : INSTR_TIME_SET_CURRENT(before);
1522 :
1523 : /* if we're not in a transaction, start one */
1524 11 : if (PQtransactionStatus(pset.db) == PQTRANS_IDLE)
1525 : {
1526 11 : results = PQexec(pset.db, "BEGIN");
1527 22 : OK = AcceptResult(results) &&
1528 11 : (PQresultStatus(results) == PGRES_COMMAND_OK);
1529 11 : ClearOrSaveResult(results);
1530 11 : if (!OK)
1531 0 : return false;
1532 11 : started_txn = true;
1533 : }
1534 :
1535 : /* Send DECLARE CURSOR */
1536 11 : initPQExpBuffer(&buf);
1537 11 : appendPQExpBuffer(&buf, "DECLARE _psql_cursor NO SCROLL CURSOR FOR\n%s",
1538 : query);
1539 :
1540 11 : results = PQexec(pset.db, buf.data);
1541 22 : OK = AcceptResult(results) &&
1542 11 : (PQresultStatus(results) == PGRES_COMMAND_OK);
1543 11 : ClearOrSaveResult(results);
1544 11 : termPQExpBuffer(&buf);
1545 11 : if (!OK)
1546 0 : goto cleanup;
1547 :
1548 11 : if (pset.timing)
1549 : {
1550 0 : INSTR_TIME_SET_CURRENT(after);
1551 0 : INSTR_TIME_SUBTRACT(after, before);
1552 0 : *elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
1553 : }
1554 :
1555 : /*
1556 : * In \gset mode, we force the fetch count to be 2, so that we will throw
1557 : * the appropriate error if the query returns more than one row.
1558 : */
1559 11 : if (pset.gset_prefix)
1560 4 : fetch_count = 2;
1561 : else
1562 7 : fetch_count = pset.fetch_count;
1563 :
1564 11 : snprintf(fetch_cmd, sizeof(fetch_cmd),
1565 : "FETCH FORWARD %d FROM _psql_cursor",
1566 : fetch_count);
1567 :
1568 : /* one-shot expanded output requested via \gx */
1569 11 : if (pset.g_expanded)
1570 2 : my_popt.topt.expanded = 1;
1571 :
1572 : /* prepare to write output to \g argument, if any */
1573 11 : if (pset.gfname)
1574 : {
1575 0 : if (!openQueryOutputFile(pset.gfname, &fout, &is_pipe))
1576 : {
1577 0 : OK = false;
1578 0 : goto cleanup;
1579 : }
1580 0 : if (is_pipe)
1581 0 : disable_sigpipe_trap();
1582 : }
1583 : else
1584 : {
1585 11 : fout = pset.queryFout;
1586 11 : is_pipe = false; /* doesn't matter */
1587 : }
1588 :
1589 : /* clear any pre-existing error indication on the output stream */
1590 11 : clearerr(fout);
1591 :
1592 : for (;;)
1593 : {
1594 21 : if (pset.timing)
1595 0 : INSTR_TIME_SET_CURRENT(before);
1596 :
1597 : /* get fetch_count tuples at a time */
1598 21 : results = PQexec(pset.db, fetch_cmd);
1599 :
1600 21 : if (pset.timing)
1601 : {
1602 0 : INSTR_TIME_SET_CURRENT(after);
1603 0 : INSTR_TIME_SUBTRACT(after, before);
1604 0 : *elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
1605 : }
1606 :
1607 21 : if (PQresultStatus(results) != PGRES_TUPLES_OK)
1608 : {
1609 : /* shut down pager before printing error message */
1610 0 : if (is_pager)
1611 : {
1612 0 : ClosePager(fout);
1613 0 : is_pager = false;
1614 : }
1615 :
1616 0 : OK = AcceptResult(results);
1617 0 : Assert(!OK);
1618 0 : ClearOrSaveResult(results);
1619 0 : break;
1620 : }
1621 :
1622 21 : if (pset.gset_prefix)
1623 : {
1624 : /* StoreQueryTuple will complain if not exactly one row */
1625 4 : OK = StoreQueryTuple(results);
1626 4 : ClearOrSaveResult(results);
1627 4 : break;
1628 : }
1629 :
1630 : /* Note we do not deal with \gexec or \crosstabview modes here */
1631 :
1632 17 : ntuples = PQntuples(results);
1633 :
1634 17 : if (ntuples < fetch_count)
1635 : {
1636 : /* this is the last result set, so allow footer decoration */
1637 7 : my_popt.topt.stop_table = true;
1638 : }
1639 10 : else if (fout == stdout && !is_pager)
1640 : {
1641 : /*
1642 : * If query requires multiple result sets, hack to ensure that
1643 : * only one pager instance is used for the whole mess
1644 : */
1645 7 : fout = PageOutput(INT_MAX, &(my_popt.topt));
1646 7 : is_pager = true;
1647 : }
1648 :
1649 17 : printQuery(results, &my_popt, fout, is_pager, pset.logfile);
1650 :
1651 17 : ClearOrSaveResult(results);
1652 :
1653 : /* after the first result set, disallow header decoration */
1654 17 : my_popt.topt.start_table = false;
1655 17 : my_popt.topt.prior_records += ntuples;
1656 :
1657 : /*
1658 : * Make sure to flush the output stream, so intermediate results are
1659 : * visible to the client immediately. We check the results because if
1660 : * the pager dies/exits/etc, there's no sense throwing more data at
1661 : * it.
1662 : */
1663 17 : flush_error = fflush(fout);
1664 :
1665 : /*
1666 : * Check if we are at the end, if a cancel was pressed, or if there
1667 : * were any errors either trying to flush out the results, or more
1668 : * generally on the output stream at all. If we hit any errors
1669 : * writing things to the stream, we presume $PAGER has disappeared and
1670 : * stop bothering to pull down more data.
1671 : */
1672 27 : if (ntuples < fetch_count || cancel_pressed || flush_error ||
1673 10 : ferror(fout))
1674 : break;
1675 10 : }
1676 :
1677 11 : if (pset.gfname)
1678 : {
1679 : /* close \g argument file/pipe */
1680 0 : if (is_pipe)
1681 : {
1682 0 : pclose(fout);
1683 0 : restore_sigpipe_trap();
1684 : }
1685 : else
1686 0 : fclose(fout);
1687 : }
1688 11 : else if (is_pager)
1689 : {
1690 : /* close transient pager */
1691 7 : ClosePager(fout);
1692 : }
1693 :
1694 : cleanup:
1695 11 : if (pset.timing)
1696 0 : INSTR_TIME_SET_CURRENT(before);
1697 :
1698 : /*
1699 : * We try to close the cursor on either success or failure, but on failure
1700 : * ignore the result (it's probably just a bleat about being in an aborted
1701 : * transaction)
1702 : */
1703 11 : results = PQexec(pset.db, "CLOSE _psql_cursor");
1704 11 : if (OK)
1705 : {
1706 18 : OK = AcceptResult(results) &&
1707 9 : (PQresultStatus(results) == PGRES_COMMAND_OK);
1708 9 : ClearOrSaveResult(results);
1709 : }
1710 : else
1711 2 : PQclear(results);
1712 :
1713 11 : if (started_txn)
1714 : {
1715 11 : results = PQexec(pset.db, OK ? "COMMIT" : "ROLLBACK");
1716 22 : OK &= AcceptResult(results) &&
1717 11 : (PQresultStatus(results) == PGRES_COMMAND_OK);
1718 11 : ClearOrSaveResult(results);
1719 : }
1720 :
1721 11 : if (pset.timing)
1722 : {
1723 0 : INSTR_TIME_SET_CURRENT(after);
1724 0 : INSTR_TIME_SUBTRACT(after, before);
1725 0 : *elapsed_msec += INSTR_TIME_GET_MILLISEC(after);
1726 : }
1727 :
1728 11 : return OK;
1729 : }
1730 :
1731 :
1732 : /*
1733 : * Advance the given char pointer over white space and SQL comments.
1734 : */
1735 : static const char *
1736 13 : skip_white_space(const char *query)
1737 : {
1738 13 : int cnestlevel = 0; /* slash-star comment nest level */
1739 :
1740 26 : while (*query)
1741 : {
1742 13 : int mblen = PQmblen(query, pset.encoding);
1743 :
1744 : /*
1745 : * Note: we assume the encoding is a superset of ASCII, so that for
1746 : * example "query[0] == '/'" is meaningful. However, we do NOT assume
1747 : * that the second and subsequent bytes of a multibyte character
1748 : * couldn't look like ASCII characters; so it is critical to advance
1749 : * by mblen, not 1, whenever we haven't exactly identified the
1750 : * character we are skipping over.
1751 : */
1752 13 : if (isspace((unsigned char) *query))
1753 0 : query += mblen;
1754 13 : else if (query[0] == '/' && query[1] == '*')
1755 : {
1756 0 : cnestlevel++;
1757 0 : query += 2;
1758 : }
1759 13 : else if (cnestlevel > 0 && query[0] == '*' && query[1] == '/')
1760 : {
1761 0 : cnestlevel--;
1762 0 : query += 2;
1763 : }
1764 13 : else if (cnestlevel == 0 && query[0] == '-' && query[1] == '-')
1765 : {
1766 0 : query += 2;
1767 :
1768 : /*
1769 : * We have to skip to end of line since any slash-star inside the
1770 : * -- comment does NOT start a slash-star comment.
1771 : */
1772 0 : while (*query)
1773 : {
1774 0 : if (*query == '\n')
1775 : {
1776 0 : query++;
1777 0 : break;
1778 : }
1779 0 : query += PQmblen(query, pset.encoding);
1780 : }
1781 : }
1782 13 : else if (cnestlevel > 0)
1783 0 : query += mblen;
1784 : else
1785 13 : break; /* found first token */
1786 : }
1787 :
1788 13 : return query;
1789 : }
1790 :
1791 :
1792 : /*
1793 : * Check whether a command is one of those for which we should NOT start
1794 : * a new transaction block (ie, send a preceding BEGIN).
1795 : *
1796 : * These include the transaction control statements themselves, plus
1797 : * certain statements that the backend disallows inside transaction blocks.
1798 : */
1799 : static bool
1800 0 : command_no_begin(const char *query)
1801 : {
1802 : int wordlen;
1803 :
1804 : /*
1805 : * First we must advance over any whitespace and comments.
1806 : */
1807 0 : query = skip_white_space(query);
1808 :
1809 : /*
1810 : * Check word length (since "beginx" is not "begin").
1811 : */
1812 0 : wordlen = 0;
1813 0 : while (isalpha((unsigned char) query[wordlen]))
1814 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1815 :
1816 : /*
1817 : * Transaction control commands. These should include every keyword that
1818 : * gives rise to a TransactionStmt in the backend grammar, except for the
1819 : * savepoint-related commands.
1820 : *
1821 : * (We assume that START must be START TRANSACTION, since there is
1822 : * presently no other "START foo" command.)
1823 : */
1824 0 : if (wordlen == 5 && pg_strncasecmp(query, "abort", 5) == 0)
1825 0 : return true;
1826 0 : if (wordlen == 5 && pg_strncasecmp(query, "begin", 5) == 0)
1827 0 : return true;
1828 0 : if (wordlen == 5 && pg_strncasecmp(query, "start", 5) == 0)
1829 0 : return true;
1830 0 : if (wordlen == 6 && pg_strncasecmp(query, "commit", 6) == 0)
1831 0 : return true;
1832 0 : if (wordlen == 3 && pg_strncasecmp(query, "end", 3) == 0)
1833 0 : return true;
1834 0 : if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)
1835 0 : return true;
1836 0 : if (wordlen == 7 && pg_strncasecmp(query, "prepare", 7) == 0)
1837 : {
1838 : /* PREPARE TRANSACTION is a TC command, PREPARE foo is not */
1839 0 : query += wordlen;
1840 :
1841 0 : query = skip_white_space(query);
1842 :
1843 0 : wordlen = 0;
1844 0 : while (isalpha((unsigned char) query[wordlen]))
1845 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1846 :
1847 0 : if (wordlen == 11 && pg_strncasecmp(query, "transaction", 11) == 0)
1848 0 : return true;
1849 0 : return false;
1850 : }
1851 :
1852 : /*
1853 : * Commands not allowed within transactions. The statements checked for
1854 : * here should be exactly those that call PreventTransactionChain() in the
1855 : * backend.
1856 : */
1857 0 : if (wordlen == 6 && pg_strncasecmp(query, "vacuum", 6) == 0)
1858 0 : return true;
1859 0 : if (wordlen == 7 && pg_strncasecmp(query, "cluster", 7) == 0)
1860 : {
1861 : /* CLUSTER with any arguments is allowed in transactions */
1862 0 : query += wordlen;
1863 :
1864 0 : query = skip_white_space(query);
1865 :
1866 0 : if (isalpha((unsigned char) query[0]))
1867 0 : return false; /* has additional words */
1868 0 : return true; /* it's CLUSTER without arguments */
1869 : }
1870 :
1871 0 : if (wordlen == 6 && pg_strncasecmp(query, "create", 6) == 0)
1872 : {
1873 0 : query += wordlen;
1874 :
1875 0 : query = skip_white_space(query);
1876 :
1877 0 : wordlen = 0;
1878 0 : while (isalpha((unsigned char) query[wordlen]))
1879 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1880 :
1881 0 : if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0)
1882 0 : return true;
1883 0 : if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0)
1884 0 : return true;
1885 :
1886 : /* CREATE [UNIQUE] INDEX CONCURRENTLY isn't allowed in xacts */
1887 0 : if (wordlen == 6 && pg_strncasecmp(query, "unique", 6) == 0)
1888 : {
1889 0 : query += wordlen;
1890 :
1891 0 : query = skip_white_space(query);
1892 :
1893 0 : wordlen = 0;
1894 0 : while (isalpha((unsigned char) query[wordlen]))
1895 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1896 : }
1897 :
1898 0 : if (wordlen == 5 && pg_strncasecmp(query, "index", 5) == 0)
1899 : {
1900 0 : query += wordlen;
1901 :
1902 0 : query = skip_white_space(query);
1903 :
1904 0 : wordlen = 0;
1905 0 : while (isalpha((unsigned char) query[wordlen]))
1906 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1907 :
1908 0 : if (wordlen == 12 && pg_strncasecmp(query, "concurrently", 12) == 0)
1909 0 : return true;
1910 : }
1911 :
1912 0 : return false;
1913 : }
1914 :
1915 0 : if (wordlen == 5 && pg_strncasecmp(query, "alter", 5) == 0)
1916 : {
1917 0 : query += wordlen;
1918 :
1919 0 : query = skip_white_space(query);
1920 :
1921 0 : wordlen = 0;
1922 0 : while (isalpha((unsigned char) query[wordlen]))
1923 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1924 :
1925 : /* ALTER SYSTEM isn't allowed in xacts */
1926 0 : if (wordlen == 6 && pg_strncasecmp(query, "system", 6) == 0)
1927 0 : return true;
1928 :
1929 0 : return false;
1930 : }
1931 :
1932 : /*
1933 : * Note: these tests will match DROP SYSTEM and REINDEX TABLESPACE, which
1934 : * aren't really valid commands so we don't care much. The other four
1935 : * possible matches are correct.
1936 : */
1937 0 : if ((wordlen == 4 && pg_strncasecmp(query, "drop", 4) == 0) ||
1938 0 : (wordlen == 7 && pg_strncasecmp(query, "reindex", 7) == 0))
1939 : {
1940 0 : query += wordlen;
1941 :
1942 0 : query = skip_white_space(query);
1943 :
1944 0 : wordlen = 0;
1945 0 : while (isalpha((unsigned char) query[wordlen]))
1946 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1947 :
1948 0 : if (wordlen == 8 && pg_strncasecmp(query, "database", 8) == 0)
1949 0 : return true;
1950 0 : if (wordlen == 6 && pg_strncasecmp(query, "system", 6) == 0)
1951 0 : return true;
1952 0 : if (wordlen == 10 && pg_strncasecmp(query, "tablespace", 10) == 0)
1953 0 : return true;
1954 :
1955 : /* DROP INDEX CONCURRENTLY isn't allowed in xacts */
1956 0 : if (wordlen == 5 && pg_strncasecmp(query, "index", 5) == 0)
1957 : {
1958 0 : query += wordlen;
1959 :
1960 0 : query = skip_white_space(query);
1961 :
1962 0 : wordlen = 0;
1963 0 : while (isalpha((unsigned char) query[wordlen]))
1964 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1965 :
1966 0 : if (wordlen == 12 && pg_strncasecmp(query, "concurrently", 12) == 0)
1967 0 : return true;
1968 :
1969 0 : return false;
1970 : }
1971 :
1972 0 : return false;
1973 : }
1974 :
1975 : /* DISCARD ALL isn't allowed in xacts, but other variants are allowed. */
1976 0 : if (wordlen == 7 && pg_strncasecmp(query, "discard", 7) == 0)
1977 : {
1978 0 : query += wordlen;
1979 :
1980 0 : query = skip_white_space(query);
1981 :
1982 0 : wordlen = 0;
1983 0 : while (isalpha((unsigned char) query[wordlen]))
1984 0 : wordlen += PQmblen(&query[wordlen], pset.encoding);
1985 :
1986 0 : if (wordlen == 3 && pg_strncasecmp(query, "all", 3) == 0)
1987 0 : return true;
1988 0 : return false;
1989 : }
1990 :
1991 0 : return false;
1992 : }
1993 :
1994 :
1995 : /*
1996 : * Check whether the specified command is a SELECT (or VALUES).
1997 : */
1998 : static bool
1999 13 : is_select_command(const char *query)
2000 : {
2001 : int wordlen;
2002 :
2003 : /*
2004 : * First advance over any whitespace, comments and left parentheses.
2005 : */
2006 : for (;;)
2007 : {
2008 13 : query = skip_white_space(query);
2009 13 : if (query[0] == '(')
2010 0 : query++;
2011 : else
2012 13 : break;
2013 0 : }
2014 :
2015 : /*
2016 : * Check word length (since "selectx" is not "select").
2017 : */
2018 13 : wordlen = 0;
2019 100 : while (isalpha((unsigned char) query[wordlen]))
2020 74 : wordlen += PQmblen(&query[wordlen], pset.encoding);
2021 :
2022 13 : if (wordlen == 6 && pg_strncasecmp(query, "select", 6) == 0)
2023 11 : return true;
2024 :
2025 2 : if (wordlen == 6 && pg_strncasecmp(query, "values", 6) == 0)
2026 0 : return true;
2027 :
2028 2 : return false;
2029 : }
2030 :
2031 :
2032 : /*
2033 : * Test if the current user is a database superuser.
2034 : *
2035 : * Note: this will correctly detect superuserness only with a protocol-3.0
2036 : * or newer backend; otherwise it will always say "false".
2037 : */
2038 : bool
2039 0 : is_superuser(void)
2040 : {
2041 : const char *val;
2042 :
2043 0 : if (!pset.db)
2044 0 : return false;
2045 :
2046 0 : val = PQparameterStatus(pset.db, "is_superuser");
2047 :
2048 0 : if (val && strcmp(val, "on") == 0)
2049 0 : return true;
2050 :
2051 0 : return false;
2052 : }
2053 :
2054 :
2055 : /*
2056 : * Test if the current session uses standard string literals.
2057 : *
2058 : * Note: With a pre-protocol-3.0 connection this will always say "false",
2059 : * which should be the right answer.
2060 : */
2061 : bool
2062 47086 : standard_strings(void)
2063 : {
2064 : const char *val;
2065 :
2066 47086 : if (!pset.db)
2067 0 : return false;
2068 :
2069 47086 : val = PQparameterStatus(pset.db, "standard_conforming_strings");
2070 :
2071 47086 : if (val && strcmp(val, "on") == 0)
2072 47025 : return true;
2073 :
2074 61 : return false;
2075 : }
2076 :
2077 :
2078 : /*
2079 : * Return the session user of the current connection.
2080 : *
2081 : * Note: this will correctly detect the session user only with a
2082 : * protocol-3.0 or newer backend; otherwise it will return the
2083 : * connection user.
2084 : */
2085 : const char *
2086 0 : session_username(void)
2087 : {
2088 : const char *val;
2089 :
2090 0 : if (!pset.db)
2091 0 : return NULL;
2092 :
2093 0 : val = PQparameterStatus(pset.db, "session_authorization");
2094 0 : if (val)
2095 0 : return val;
2096 : else
2097 0 : return PQuser(pset.db);
2098 : }
2099 :
2100 :
2101 : /* expand_tilde
2102 : *
2103 : * substitute '~' with HOME or '~username' with username's home dir
2104 : *
2105 : */
2106 : void
2107 3 : expand_tilde(char **filename)
2108 : {
2109 3 : if (!filename || !(*filename))
2110 0 : return;
2111 :
2112 : /*
2113 : * WIN32 doesn't use tilde expansion for file names. Also, it uses tilde
2114 : * for short versions of long file names, though the tilde is usually
2115 : * toward the end, not at the beginning.
2116 : */
2117 : #ifndef WIN32
2118 :
2119 : /* try tilde expansion */
2120 3 : if (**filename == '~')
2121 : {
2122 : char *fn;
2123 : char oldp,
2124 : *p;
2125 : struct passwd *pw;
2126 : char home[MAXPGPATH];
2127 :
2128 0 : fn = *filename;
2129 0 : *home = '\0';
2130 :
2131 0 : p = fn + 1;
2132 0 : while (*p != '/' && *p != '\0')
2133 0 : p++;
2134 :
2135 0 : oldp = *p;
2136 0 : *p = '\0';
2137 :
2138 0 : if (*(fn + 1) == '\0')
2139 0 : get_home_path(home); /* ~ or ~/ only */
2140 0 : else if ((pw = getpwnam(fn + 1)) != NULL)
2141 0 : strlcpy(home, pw->pw_dir, sizeof(home)); /* ~user */
2142 :
2143 0 : *p = oldp;
2144 0 : if (strlen(home) != 0)
2145 : {
2146 : char *newfn;
2147 :
2148 0 : newfn = psprintf("%s%s", home, p);
2149 0 : free(fn);
2150 0 : *filename = newfn;
2151 : }
2152 : }
2153 : #endif
2154 :
2155 3 : return;
2156 : }
2157 :
2158 : /*
2159 : * Checks if connection string starts with either of the valid URI prefix
2160 : * designators.
2161 : *
2162 : * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix.
2163 : *
2164 : * XXX This is a duplicate of the eponymous libpq function.
2165 : */
2166 : static int
2167 0 : uri_prefix_length(const char *connstr)
2168 : {
2169 : /* The connection URI must start with either of the following designators: */
2170 : static const char uri_designator[] = "postgresql://";
2171 : static const char short_uri_designator[] = "postgres://";
2172 :
2173 0 : if (strncmp(connstr, uri_designator,
2174 : sizeof(uri_designator) - 1) == 0)
2175 0 : return sizeof(uri_designator) - 1;
2176 :
2177 0 : if (strncmp(connstr, short_uri_designator,
2178 : sizeof(short_uri_designator) - 1) == 0)
2179 0 : return sizeof(short_uri_designator) - 1;
2180 :
2181 0 : return 0;
2182 : }
2183 :
2184 : /*
2185 : * Recognized connection string either starts with a valid URI prefix or
2186 : * contains a "=" in it.
2187 : *
2188 : * Must be consistent with parse_connection_string: anything for which this
2189 : * returns true should at least look like it's parseable by that routine.
2190 : *
2191 : * XXX This is a duplicate of the eponymous libpq function.
2192 : */
2193 : bool
2194 0 : recognized_connection_string(const char *connstr)
2195 : {
2196 0 : return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL;
2197 : }
|