Line data Source code
1 : /*
2 : * psql - the PostgreSQL interactive terminal
3 : *
4 : * Copyright (c) 2000-2024, PostgreSQL Global Development Group
5 : *
6 : * src/bin/psql/startup.c
7 : */
8 : #include "postgres_fe.h"
9 :
10 : #ifndef WIN32
11 : #include <unistd.h>
12 : #else /* WIN32 */
13 : #include <io.h>
14 : #include <win32.h>
15 : #endif /* WIN32 */
16 :
17 : #include "command.h"
18 : #include "common.h"
19 : #include "common/logging.h"
20 : #include "common/string.h"
21 : #include "describe.h"
22 : #include "fe_utils/print.h"
23 : #include "getopt_long.h"
24 : #include "help.h"
25 : #include "input.h"
26 : #include "mainloop.h"
27 : #include "settings.h"
28 :
29 : /*
30 : * Global psql options
31 : */
32 : PsqlSettings pset;
33 :
34 : #ifndef WIN32
35 : #define SYSPSQLRC "psqlrc"
36 : #define PSQLRC ".psqlrc"
37 : #else
38 : #define SYSPSQLRC "psqlrc"
39 : #define PSQLRC "psqlrc.conf"
40 : #endif
41 :
42 : /*
43 : * Structures to pass information between the option parsing routine
44 : * and the main function
45 : */
46 : enum _actions
47 : {
48 : ACT_SINGLE_QUERY,
49 : ACT_SINGLE_SLASH,
50 : ACT_FILE,
51 : };
52 :
53 : typedef struct SimpleActionListCell
54 : {
55 : struct SimpleActionListCell *next;
56 : enum _actions action;
57 : char *val;
58 : } SimpleActionListCell;
59 :
60 : typedef struct SimpleActionList
61 : {
62 : SimpleActionListCell *head;
63 : SimpleActionListCell *tail;
64 : } SimpleActionList;
65 :
66 : struct adhoc_opts
67 : {
68 : char *dbname;
69 : char *host;
70 : char *port;
71 : char *username;
72 : char *logfilename;
73 : bool no_readline;
74 : bool no_psqlrc;
75 : bool single_txn;
76 : bool list_dbs;
77 : SimpleActionList actions;
78 : };
79 :
80 : static void parse_psql_options(int argc, char *argv[],
81 : struct adhoc_opts *options);
82 : static void simple_action_list_append(SimpleActionList *list,
83 : enum _actions action, const char *val);
84 : static void process_psqlrc(char *argv0);
85 : static void process_psqlrc_file(char *filename);
86 : static void showVersion(void);
87 : static void EstablishVariableSpace(void);
88 :
89 : #define NOPAGER 0
90 :
91 : static void
92 194790 : log_pre_callback(void)
93 : {
94 194790 : if (pset.queryFout && pset.queryFout != stdout)
95 0 : fflush(pset.queryFout);
96 194790 : }
97 :
98 : static void
99 194790 : log_locus_callback(const char **filename, uint64 *lineno)
100 : {
101 194790 : if (pset.inputfile)
102 : {
103 133600 : *filename = pset.inputfile;
104 133600 : *lineno = pset.lineno;
105 : }
106 : else
107 : {
108 61190 : *filename = NULL;
109 61190 : *lineno = 0;
110 : }
111 194790 : }
112 :
113 : #ifndef WIN32
114 : static void
115 8 : empty_signal_handler(SIGNAL_ARGS)
116 : {
117 8 : }
118 : #endif
119 :
120 : /*
121 : *
122 : * main
123 : *
124 : */
125 : int
126 18686 : main(int argc, char *argv[])
127 : {
128 : struct adhoc_opts options;
129 : int successResult;
130 18686 : char *password = NULL;
131 : bool new_pass;
132 :
133 18686 : pg_logging_init(argv[0]);
134 18686 : pg_logging_set_pre_callback(log_pre_callback);
135 18686 : pg_logging_set_locus_callback(log_locus_callback);
136 18686 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("psql"));
137 :
138 18686 : if (argc > 1)
139 : {
140 18686 : if ((strcmp(argv[1], "-?") == 0) || (argc == 2 && (strcmp(argv[1], "--help") == 0)))
141 : {
142 2 : usage(NOPAGER);
143 2 : exit(EXIT_SUCCESS);
144 : }
145 18684 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
146 : {
147 20 : showVersion();
148 20 : exit(EXIT_SUCCESS);
149 : }
150 : }
151 :
152 18664 : pset.progname = get_progname(argv[0]);
153 :
154 18664 : pset.db = NULL;
155 18664 : pset.dead_conn = NULL;
156 18664 : setDecimalLocale();
157 18664 : pset.encoding = PQenv2encoding();
158 18664 : pset.queryFout = stdout;
159 18664 : pset.queryFoutPipe = false;
160 18664 : pset.copyStream = NULL;
161 18664 : pset.last_error_result = NULL;
162 18664 : pset.cur_cmd_source = stdin;
163 18664 : pset.cur_cmd_interactive = false;
164 :
165 : /* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */
166 18664 : pset.popt.topt.format = PRINT_ALIGNED;
167 18664 : pset.popt.topt.border = 1;
168 18664 : pset.popt.topt.pager = 1;
169 18664 : pset.popt.topt.pager_min_lines = 0;
170 18664 : pset.popt.topt.start_table = true;
171 18664 : pset.popt.topt.stop_table = true;
172 18664 : pset.popt.topt.default_footer = true;
173 :
174 18664 : pset.popt.topt.csvFieldSep[0] = DEFAULT_CSV_FIELD_SEP;
175 18664 : pset.popt.topt.csvFieldSep[1] = '\0';
176 :
177 18664 : pset.popt.topt.unicode_border_linestyle = UNICODE_LINESTYLE_SINGLE;
178 18664 : pset.popt.topt.unicode_column_linestyle = UNICODE_LINESTYLE_SINGLE;
179 18664 : pset.popt.topt.unicode_header_linestyle = UNICODE_LINESTYLE_SINGLE;
180 :
181 18664 : refresh_utf8format(&(pset.popt.topt));
182 :
183 : /* We must get COLUMNS here before readline() sets it */
184 18664 : pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0;
185 :
186 18664 : pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
187 :
188 18664 : pset.getPassword = TRI_DEFAULT;
189 :
190 18664 : EstablishVariableSpace();
191 :
192 : /* Create variables showing psql version number */
193 18664 : SetVariable(pset.vars, "VERSION", PG_VERSION_STR);
194 18664 : SetVariable(pset.vars, "VERSION_NAME", PG_VERSION);
195 18664 : SetVariable(pset.vars, "VERSION_NUM", CppAsString2(PG_VERSION_NUM));
196 :
197 : /* Initialize variables for last error */
198 18664 : SetVariable(pset.vars, "LAST_ERROR_MESSAGE", "");
199 18664 : SetVariable(pset.vars, "LAST_ERROR_SQLSTATE", "00000");
200 :
201 : /* Default values for variables (that don't match the result of \unset) */
202 18664 : SetVariableBool(pset.vars, "AUTOCOMMIT");
203 18664 : SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1);
204 18664 : SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
205 18664 : SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
206 18664 : SetVariableBool(pset.vars, "SHOW_ALL_RESULTS");
207 :
208 18664 : parse_psql_options(argc, argv, &options);
209 :
210 : /*
211 : * If no action was specified and we're in non-interactive mode, treat it
212 : * as if the user had specified "-f -". This lets single-transaction mode
213 : * work in this case.
214 : */
215 18658 : if (options.actions.head == NULL && pset.notty)
216 6986 : simple_action_list_append(&options.actions, ACT_FILE, NULL);
217 :
218 : /* Bail out if -1 was specified but will be ignored. */
219 18658 : if (options.single_txn && options.actions.head == NULL)
220 0 : pg_fatal("-1 can only be used in non-interactive mode");
221 :
222 18658 : if (!pset.popt.topt.fieldSep.separator &&
223 18654 : !pset.popt.topt.fieldSep.separator_zero)
224 : {
225 18654 : pset.popt.topt.fieldSep.separator = pg_strdup(DEFAULT_FIELD_SEP);
226 18654 : pset.popt.topt.fieldSep.separator_zero = false;
227 : }
228 18658 : if (!pset.popt.topt.recordSep.separator &&
229 18658 : !pset.popt.topt.recordSep.separator_zero)
230 : {
231 18658 : pset.popt.topt.recordSep.separator = pg_strdup(DEFAULT_RECORD_SEP);
232 18658 : pset.popt.topt.recordSep.separator_zero = false;
233 : }
234 :
235 18658 : if (pset.getPassword == TRI_YES)
236 : {
237 : /*
238 : * We can't be sure yet of the username that will be used, so don't
239 : * offer a potentially wrong one. Typical uses of this option are
240 : * noninteractive anyway. (Note: since we've not yet set up our
241 : * cancel handler, there's no need to use simple_prompt_extended.)
242 : */
243 0 : password = simple_prompt("Password: ", false);
244 : }
245 :
246 : /* loop until we have a password if requested by backend */
247 : do
248 : {
249 : #define PARAMS_ARRAY_SIZE 8
250 18658 : const char **keywords = pg_malloc_array(const char *, PARAMS_ARRAY_SIZE);
251 18658 : const char **values = pg_malloc_array(const char *, PARAMS_ARRAY_SIZE);
252 :
253 18658 : keywords[0] = "host";
254 18658 : values[0] = options.host;
255 18658 : keywords[1] = "port";
256 18658 : values[1] = options.port;
257 18658 : keywords[2] = "user";
258 18658 : values[2] = options.username;
259 18658 : keywords[3] = "password";
260 18658 : values[3] = password;
261 18658 : keywords[4] = "dbname"; /* see do_connect() */
262 0 : values[4] = (options.list_dbs && options.dbname == NULL) ?
263 18658 : "postgres" : options.dbname;
264 18658 : keywords[5] = "fallback_application_name";
265 18658 : values[5] = pset.progname;
266 18658 : keywords[6] = "client_encoding";
267 18658 : values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto";
268 18658 : keywords[7] = NULL;
269 18658 : values[7] = NULL;
270 :
271 18658 : new_pass = false;
272 18658 : pset.db = PQconnectdbParams(keywords, values, true);
273 18658 : free(keywords);
274 18658 : free(values);
275 :
276 18986 : if (PQstatus(pset.db) == CONNECTION_BAD &&
277 330 : PQconnectionNeedsPassword(pset.db) &&
278 2 : !password &&
279 2 : pset.getPassword != TRI_NO)
280 : {
281 : /*
282 : * Before closing the old PGconn, extract the user name that was
283 : * actually connected with --- it might've come out of a URI or
284 : * connstring "database name" rather than options.username.
285 : */
286 0 : const char *realusername = PQuser(pset.db);
287 : char *password_prompt;
288 :
289 0 : if (realusername && realusername[0])
290 0 : password_prompt = psprintf(_("Password for user %s: "),
291 : realusername);
292 : else
293 0 : password_prompt = pg_strdup(_("Password: "));
294 0 : PQfinish(pset.db);
295 :
296 0 : password = simple_prompt(password_prompt, false);
297 0 : free(password_prompt);
298 0 : new_pass = true;
299 : }
300 18658 : } while (new_pass);
301 :
302 18658 : if (PQstatus(pset.db) == CONNECTION_BAD)
303 : {
304 328 : pg_log_error("%s", PQerrorMessage(pset.db));
305 328 : PQfinish(pset.db);
306 328 : exit(EXIT_BADCONN);
307 : }
308 :
309 18330 : psql_setup_cancel_handler();
310 :
311 : #ifndef WIN32
312 :
313 : /*
314 : * do_watch() needs signal handlers installed (otherwise sigwait() will
315 : * filter them out on some platforms), but doesn't need them to do
316 : * anything, and they shouldn't ever run (unless perhaps a stray SIGALRM
317 : * arrives due to a race when do_watch() cancels an itimer).
318 : */
319 18330 : pqsignal(SIGCHLD, empty_signal_handler);
320 18330 : pqsignal(SIGALRM, empty_signal_handler);
321 : #endif
322 :
323 18330 : PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
324 :
325 18330 : SyncVariables();
326 :
327 18330 : if (options.list_dbs)
328 : {
329 : int success;
330 :
331 0 : if (!options.no_psqlrc)
332 0 : process_psqlrc(argv[0]);
333 :
334 0 : success = listAllDbs(NULL, false);
335 0 : PQfinish(pset.db);
336 0 : exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
337 : }
338 :
339 18330 : if (options.logfilename)
340 : {
341 0 : pset.logfile = fopen(options.logfilename, "a");
342 0 : if (!pset.logfile)
343 0 : pg_fatal("could not open log file \"%s\": %m",
344 : options.logfilename);
345 : }
346 :
347 18330 : if (!options.no_psqlrc)
348 0 : process_psqlrc(argv[0]);
349 :
350 : /*
351 : * If any actions were given by user, process them in the order in which
352 : * they were specified. Note single_txn is only effective in this mode.
353 : */
354 18330 : if (options.actions.head != NULL)
355 : {
356 : PGresult *res;
357 : SimpleActionListCell *cell;
358 :
359 18326 : successResult = EXIT_SUCCESS; /* silence compiler */
360 :
361 18326 : if (options.single_txn)
362 : {
363 14 : if ((res = PSQLexec("BEGIN")) == NULL)
364 : {
365 0 : if (pset.on_error_stop)
366 : {
367 0 : successResult = EXIT_USER;
368 0 : goto error;
369 : }
370 : }
371 : else
372 14 : PQclear(res);
373 : }
374 :
375 36814 : for (cell = options.actions.head; cell; cell = cell->next)
376 : {
377 18624 : if (cell->action == ACT_SINGLE_QUERY)
378 : {
379 788 : pg_logging_config(PG_LOG_FLAG_TERSE);
380 :
381 788 : if (pset.echo == PSQL_ECHO_ALL)
382 0 : puts(cell->val);
383 :
384 788 : successResult = SendQuery(cell->val)
385 786 : ? EXIT_SUCCESS : EXIT_FAILURE;
386 : }
387 17836 : else if (cell->action == ACT_SINGLE_SLASH)
388 : {
389 : PsqlScanState scan_state;
390 : ConditionalStack cond_stack;
391 :
392 4 : pg_logging_config(PG_LOG_FLAG_TERSE);
393 :
394 4 : if (pset.echo == PSQL_ECHO_ALL)
395 0 : puts(cell->val);
396 :
397 4 : scan_state = psql_scan_create(&psqlscan_callbacks);
398 4 : psql_scan_setup(scan_state,
399 4 : cell->val, strlen(cell->val),
400 4 : pset.encoding, standard_strings());
401 4 : cond_stack = conditional_stack_create();
402 4 : psql_scan_set_passthrough(scan_state, (void *) cond_stack);
403 :
404 4 : successResult = HandleSlashCmds(scan_state,
405 : cond_stack,
406 : NULL,
407 : NULL) != PSQL_CMD_ERROR
408 4 : ? EXIT_SUCCESS : EXIT_FAILURE;
409 :
410 4 : psql_scan_destroy(scan_state);
411 4 : conditional_stack_destroy(cond_stack);
412 : }
413 17832 : else if (cell->action == ACT_FILE)
414 : {
415 17832 : successResult = process_file(cell->val, false);
416 : }
417 : else
418 : {
419 : /* should never come here */
420 : Assert(false);
421 : }
422 :
423 18602 : if (successResult != EXIT_SUCCESS && pset.on_error_stop)
424 114 : break;
425 : }
426 :
427 18304 : if (options.single_txn)
428 : {
429 : /*
430 : * Rollback the contents of the single transaction if the caller
431 : * has set ON_ERROR_STOP and one of the steps has failed. This
432 : * check needs to match the one done a couple of lines above.
433 : */
434 14 : res = PSQLexec((successResult != EXIT_SUCCESS && pset.on_error_stop) ?
435 : "ROLLBACK" : "COMMIT");
436 14 : if (res == NULL)
437 : {
438 0 : if (pset.on_error_stop)
439 : {
440 0 : successResult = EXIT_USER;
441 0 : goto error;
442 : }
443 : }
444 : else
445 14 : PQclear(res);
446 : }
447 :
448 18304 : error:
449 : ;
450 : }
451 :
452 : /*
453 : * or otherwise enter interactive main loop
454 : */
455 : else
456 : {
457 4 : pg_logging_config(PG_LOG_FLAG_TERSE);
458 4 : connection_warnings(true);
459 4 : if (!pset.quiet)
460 4 : printf(_("Type \"help\" for help.\n\n"));
461 4 : initializeInput(options.no_readline ? 0 : 1);
462 4 : successResult = MainLoop(stdin);
463 : }
464 :
465 : /* clean up */
466 18308 : if (pset.logfile)
467 0 : fclose(pset.logfile);
468 18308 : if (pset.db)
469 18308 : PQfinish(pset.db);
470 18308 : if (pset.dead_conn)
471 0 : PQfinish(pset.dead_conn);
472 18308 : setQFout(NULL);
473 :
474 18308 : return successResult;
475 : }
476 :
477 :
478 : /*
479 : * Parse command line options
480 : */
481 :
482 : static void
483 18664 : parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
484 : {
485 : static struct option long_options[] =
486 : {
487 : {"echo-all", no_argument, NULL, 'a'},
488 : {"no-align", no_argument, NULL, 'A'},
489 : {"command", required_argument, NULL, 'c'},
490 : {"dbname", required_argument, NULL, 'd'},
491 : {"echo-queries", no_argument, NULL, 'e'},
492 : {"echo-errors", no_argument, NULL, 'b'},
493 : {"echo-hidden", no_argument, NULL, 'E'},
494 : {"file", required_argument, NULL, 'f'},
495 : {"field-separator", required_argument, NULL, 'F'},
496 : {"field-separator-zero", no_argument, NULL, 'z'},
497 : {"host", required_argument, NULL, 'h'},
498 : {"html", no_argument, NULL, 'H'},
499 : {"list", no_argument, NULL, 'l'},
500 : {"log-file", required_argument, NULL, 'L'},
501 : {"no-readline", no_argument, NULL, 'n'},
502 : {"single-transaction", no_argument, NULL, '1'},
503 : {"output", required_argument, NULL, 'o'},
504 : {"port", required_argument, NULL, 'p'},
505 : {"pset", required_argument, NULL, 'P'},
506 : {"quiet", no_argument, NULL, 'q'},
507 : {"record-separator", required_argument, NULL, 'R'},
508 : {"record-separator-zero", no_argument, NULL, '0'},
509 : {"single-step", no_argument, NULL, 's'},
510 : {"single-line", no_argument, NULL, 'S'},
511 : {"tuples-only", no_argument, NULL, 't'},
512 : {"table-attr", required_argument, NULL, 'T'},
513 : {"username", required_argument, NULL, 'U'},
514 : {"set", required_argument, NULL, 'v'},
515 : {"variable", required_argument, NULL, 'v'},
516 : {"version", no_argument, NULL, 'V'},
517 : {"no-password", no_argument, NULL, 'w'},
518 : {"password", no_argument, NULL, 'W'},
519 : {"expanded", no_argument, NULL, 'x'},
520 : {"no-psqlrc", no_argument, NULL, 'X'},
521 : {"help", optional_argument, NULL, 1},
522 : {"csv", no_argument, NULL, 2},
523 : {NULL, 0, NULL, 0}
524 : };
525 :
526 : int optindex;
527 : int c;
528 :
529 18664 : memset(options, 0, sizeof *options);
530 :
531 131070 : while ((c = getopt_long(argc, argv, "aAbc:d:eEf:F:h:HlL:no:p:P:qR:sStT:U:v:VwWxXz?01",
532 : long_options, &optindex)) != -1)
533 : {
534 112412 : switch (c)
535 : {
536 1928 : case 'a':
537 1928 : SetVariable(pset.vars, "ECHO", "all");
538 1928 : break;
539 16252 : case 'A':
540 16252 : pset.popt.topt.format = PRINT_UNALIGNED;
541 16252 : break;
542 0 : case 'b':
543 0 : SetVariable(pset.vars, "ECHO", "errors");
544 0 : break;
545 866 : case 'c':
546 866 : if (optarg[0] == '\\')
547 4 : simple_action_list_append(&options->actions,
548 : ACT_SINGLE_SLASH,
549 4 : optarg + 1);
550 : else
551 862 : simple_action_list_append(&options->actions,
552 : ACT_SINGLE_QUERY,
553 : optarg);
554 866 : break;
555 18482 : case 'd':
556 18482 : options->dbname = pg_strdup(optarg);
557 18482 : break;
558 6 : case 'e':
559 6 : SetVariable(pset.vars, "ECHO", "queries");
560 6 : break;
561 0 : case 'E':
562 0 : SetVariableBool(pset.vars, "ECHO_HIDDEN");
563 0 : break;
564 11174 : case 'f':
565 11174 : simple_action_list_append(&options->actions,
566 : ACT_FILE,
567 : optarg);
568 11174 : break;
569 4 : case 'F':
570 4 : pset.popt.topt.fieldSep.separator = pg_strdup(optarg);
571 4 : pset.popt.topt.fieldSep.separator_zero = false;
572 4 : break;
573 6 : case 'h':
574 6 : options->host = pg_strdup(optarg);
575 6 : break;
576 0 : case 'H':
577 0 : pset.popt.topt.format = PRINT_HTML;
578 0 : break;
579 0 : case 'l':
580 0 : options->list_dbs = true;
581 0 : break;
582 0 : case 'L':
583 0 : options->logfilename = pg_strdup(optarg);
584 0 : break;
585 0 : case 'n':
586 0 : options->no_readline = true;
587 0 : break;
588 0 : case 'o':
589 0 : if (!setQFout(optarg))
590 0 : exit(EXIT_FAILURE);
591 0 : break;
592 8 : case 'p':
593 8 : options->port = pg_strdup(optarg);
594 8 : break;
595 4 : case 'P':
596 : {
597 : char *value;
598 : char *equal_loc;
599 : bool result;
600 :
601 4 : value = pg_strdup(optarg);
602 4 : equal_loc = strchr(value, '=');
603 4 : if (!equal_loc)
604 0 : result = do_pset(value, NULL, &pset.popt, true);
605 : else
606 : {
607 4 : *equal_loc = '\0';
608 4 : result = do_pset(value, equal_loc + 1, &pset.popt, true);
609 : }
610 :
611 4 : if (!result)
612 0 : pg_fatal("could not set printing parameter \"%s\"", value);
613 :
614 4 : free(value);
615 4 : break;
616 : }
617 13554 : case 'q':
618 13554 : SetVariableBool(pset.vars, "QUIET");
619 13554 : break;
620 0 : case 'R':
621 0 : pset.popt.topt.recordSep.separator = pg_strdup(optarg);
622 0 : pset.popt.topt.recordSep.separator_zero = false;
623 0 : break;
624 0 : case 's':
625 0 : SetVariableBool(pset.vars, "SINGLESTEP");
626 0 : break;
627 0 : case 'S':
628 0 : SetVariableBool(pset.vars, "SINGLELINE");
629 0 : break;
630 16238 : case 't':
631 16238 : pset.popt.topt.tuples_only = true;
632 16238 : break;
633 0 : case 'T':
634 0 : pset.popt.topt.tableAttr = pg_strdup(optarg);
635 0 : break;
636 22 : case 'U':
637 22 : options->username = pg_strdup(optarg);
638 22 : break;
639 14466 : case 'v':
640 : {
641 : char *value;
642 : char *equal_loc;
643 :
644 14466 : value = pg_strdup(optarg);
645 14466 : equal_loc = strchr(value, '=');
646 14466 : if (!equal_loc)
647 : {
648 0 : if (!DeleteVariable(pset.vars, value))
649 0 : exit(EXIT_FAILURE); /* error already printed */
650 : }
651 : else
652 : {
653 14466 : *equal_loc = '\0';
654 14466 : if (!SetVariable(pset.vars, value, equal_loc + 1))
655 0 : exit(EXIT_FAILURE); /* error already printed */
656 : }
657 :
658 14466 : free(value);
659 14466 : break;
660 : }
661 0 : case 'V':
662 0 : showVersion();
663 0 : exit(EXIT_SUCCESS);
664 724 : case 'w':
665 724 : pset.getPassword = TRI_NO;
666 724 : break;
667 0 : case 'W':
668 0 : pset.getPassword = TRI_YES;
669 0 : break;
670 0 : case 'x':
671 0 : pset.popt.topt.expanded = true;
672 0 : break;
673 18658 : case 'X':
674 18658 : options->no_psqlrc = true;
675 18658 : break;
676 0 : case 'z':
677 0 : pset.popt.topt.fieldSep.separator_zero = true;
678 0 : break;
679 0 : case '0':
680 0 : pset.popt.topt.recordSep.separator_zero = true;
681 0 : break;
682 14 : case '1':
683 14 : options->single_txn = true;
684 14 : break;
685 2 : case '?':
686 2 : if (optind <= argc &&
687 2 : strcmp(argv[optind - 1], "-?") == 0)
688 : {
689 : /* actual help option given */
690 0 : usage(NOPAGER);
691 0 : exit(EXIT_SUCCESS);
692 : }
693 : else
694 : {
695 : /* getopt error (unknown option or missing argument) */
696 2 : goto unknown_option;
697 : }
698 : break;
699 4 : case 1:
700 : {
701 4 : if (!optarg || strcmp(optarg, "options") == 0)
702 0 : usage(NOPAGER);
703 4 : else if (optarg && strcmp(optarg, "commands") == 0)
704 2 : slashUsage(NOPAGER);
705 2 : else if (optarg && strcmp(optarg, "variables") == 0)
706 2 : helpVariables(NOPAGER);
707 : else
708 0 : goto unknown_option;
709 :
710 4 : exit(EXIT_SUCCESS);
711 : }
712 : break;
713 0 : case 2:
714 0 : pset.popt.topt.format = PRINT_CSV;
715 0 : break;
716 : default:
717 2 : unknown_option:
718 : /* getopt_long already emitted a complaint */
719 2 : pg_log_error_hint("Try \"%s --help\" for more information.",
720 : pset.progname);
721 2 : exit(EXIT_FAILURE);
722 : }
723 : }
724 :
725 : /*
726 : * if we still have arguments, use it as the database name and username
727 : */
728 18868 : while (argc - optind >= 1)
729 : {
730 210 : if (!options->dbname)
731 210 : options->dbname = argv[optind];
732 0 : else if (!options->username)
733 0 : options->username = argv[optind];
734 0 : else if (!pset.quiet)
735 0 : pg_log_warning("extra command-line argument \"%s\" ignored",
736 : argv[optind]);
737 :
738 210 : optind++;
739 : }
740 18658 : }
741 :
742 :
743 : /*
744 : * Append a new item to the end of the SimpleActionList.
745 : * Note that "val" is copied if it's not NULL.
746 : */
747 : static void
748 19026 : simple_action_list_append(SimpleActionList *list,
749 : enum _actions action, const char *val)
750 : {
751 : SimpleActionListCell *cell;
752 :
753 19026 : cell = pg_malloc_object(SimpleActionListCell);
754 :
755 19026 : cell->next = NULL;
756 19026 : cell->action = action;
757 19026 : if (val)
758 12040 : cell->val = pg_strdup(val);
759 : else
760 6986 : cell->val = NULL;
761 :
762 19026 : if (list->tail)
763 372 : list->tail->next = cell;
764 : else
765 18654 : list->head = cell;
766 19026 : list->tail = cell;
767 19026 : }
768 :
769 :
770 : /*
771 : * Load .psqlrc file, if found.
772 : */
773 : static void
774 0 : process_psqlrc(char *argv0)
775 : {
776 : char home[MAXPGPATH];
777 : char rc_file[MAXPGPATH];
778 : char my_exec_path[MAXPGPATH];
779 : char etc_path[MAXPGPATH];
780 0 : char *envrc = getenv("PSQLRC");
781 :
782 0 : if (find_my_exec(argv0, my_exec_path) < 0)
783 0 : pg_fatal("could not find own program executable");
784 :
785 0 : get_etc_path(my_exec_path, etc_path);
786 :
787 0 : snprintf(rc_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC);
788 0 : process_psqlrc_file(rc_file);
789 :
790 0 : if (envrc != NULL && strlen(envrc) > 0)
791 0 : {
792 : /* might need to free() this */
793 0 : char *envrc_alloc = pstrdup(envrc);
794 :
795 0 : expand_tilde(&envrc_alloc);
796 0 : process_psqlrc_file(envrc_alloc);
797 : }
798 0 : else if (get_home_path(home))
799 : {
800 0 : snprintf(rc_file, MAXPGPATH, "%s/%s", home, PSQLRC);
801 0 : process_psqlrc_file(rc_file);
802 : }
803 0 : }
804 :
805 :
806 :
807 : static void
808 0 : process_psqlrc_file(char *filename)
809 : {
810 : char *psqlrc_minor,
811 : *psqlrc_major;
812 :
813 : #if defined(WIN32) && (!defined(__MINGW32__))
814 : #define R_OK 4
815 : #endif
816 :
817 0 : psqlrc_minor = psprintf("%s-%s", filename, PG_VERSION);
818 0 : psqlrc_major = psprintf("%s-%s", filename, PG_MAJORVERSION);
819 :
820 : /* check for minor version first, then major, then no version */
821 0 : if (access(psqlrc_minor, R_OK) == 0)
822 0 : (void) process_file(psqlrc_minor, false);
823 0 : else if (access(psqlrc_major, R_OK) == 0)
824 0 : (void) process_file(psqlrc_major, false);
825 0 : else if (access(filename, R_OK) == 0)
826 0 : (void) process_file(filename, false);
827 :
828 0 : free(psqlrc_minor);
829 0 : free(psqlrc_major);
830 0 : }
831 :
832 :
833 :
834 : /* showVersion
835 : *
836 : * This output format is intended to match GNU standards.
837 : */
838 : static void
839 20 : showVersion(void)
840 : {
841 20 : puts("psql (PostgreSQL) " PG_VERSION);
842 20 : }
843 :
844 :
845 :
846 : /*
847 : * Substitute hooks and assign hooks for psql variables.
848 : *
849 : * This isn't an amazingly good place for them, but neither is anywhere else.
850 : *
851 : * By policy, every special variable that controls any psql behavior should
852 : * have one or both hooks, even if they're just no-ops. This ensures that
853 : * the variable will remain present in variables.c's list even when unset,
854 : * which ensures that it's known to tab completion.
855 : */
856 :
857 : static char *
858 252188 : bool_substitute_hook(char *newval)
859 : {
860 252188 : if (newval == NULL)
861 : {
862 : /* "\unset FOO" becomes "\set FOO off" */
863 186646 : newval = pg_strdup("off");
864 : }
865 65542 : else if (newval[0] == '\0')
866 : {
867 : /* "\set FOO" becomes "\set FOO on" */
868 6 : pg_free(newval);
869 6 : newval = pg_strdup("on");
870 : }
871 252188 : return newval;
872 : }
873 :
874 : static bool
875 37370 : autocommit_hook(const char *newval)
876 : {
877 37370 : return ParseVariableBool(newval, "AUTOCOMMIT", &pset.autocommit);
878 : }
879 :
880 : static bool
881 29274 : on_error_stop_hook(const char *newval)
882 : {
883 29274 : return ParseVariableBool(newval, "ON_ERROR_STOP", &pset.on_error_stop);
884 : }
885 :
886 : static bool
887 32290 : quiet_hook(const char *newval)
888 : {
889 32290 : return ParseVariableBool(newval, "QUIET", &pset.quiet);
890 : }
891 :
892 : static bool
893 18664 : singleline_hook(const char *newval)
894 : {
895 18664 : return ParseVariableBool(newval, "SINGLELINE", &pset.singleline);
896 : }
897 :
898 : static bool
899 18664 : singlestep_hook(const char *newval)
900 : {
901 18664 : return ParseVariableBool(newval, "SINGLESTEP", &pset.singlestep);
902 : }
903 :
904 : static char *
905 18720 : fetch_count_substitute_hook(char *newval)
906 : {
907 18720 : if (newval == NULL)
908 18688 : newval = pg_strdup("0");
909 18720 : return newval;
910 : }
911 :
912 : static bool
913 18720 : fetch_count_hook(const char *newval)
914 : {
915 18720 : return ParseVariableNum(newval, "FETCH_COUNT", &pset.fetch_count);
916 : }
917 :
918 : static bool
919 18664 : histfile_hook(const char *newval)
920 : {
921 : /*
922 : * Someday we might try to validate the filename, but for now, this is
923 : * just a placeholder to ensure HISTFILE is known to tab completion.
924 : */
925 18664 : return true;
926 : }
927 :
928 : static char *
929 18664 : histsize_substitute_hook(char *newval)
930 : {
931 18664 : if (newval == NULL)
932 18664 : newval = pg_strdup("500");
933 18664 : return newval;
934 : }
935 :
936 : static bool
937 18664 : histsize_hook(const char *newval)
938 : {
939 18664 : return ParseVariableNum(newval, "HISTSIZE", &pset.histsize);
940 : }
941 :
942 : static char *
943 18664 : ignoreeof_substitute_hook(char *newval)
944 : {
945 : int dummy;
946 :
947 : /*
948 : * This tries to mimic the behavior of bash, to wit "If set, the value is
949 : * the number of consecutive EOF characters which must be typed as the
950 : * first characters on an input line before bash exits. If the variable
951 : * exists but does not have a numeric value, or has no value, the default
952 : * value is 10. If it does not exist, EOF signifies the end of input to
953 : * the shell." Unlike bash, however, we insist on the stored value
954 : * actually being a valid integer.
955 : */
956 18664 : if (newval == NULL)
957 18664 : newval = pg_strdup("0");
958 0 : else if (!ParseVariableNum(newval, NULL, &dummy))
959 0 : newval = pg_strdup("10");
960 18664 : return newval;
961 : }
962 :
963 : static bool
964 18664 : ignoreeof_hook(const char *newval)
965 : {
966 18664 : return ParseVariableNum(newval, "IGNOREEOF", &pset.ignoreeof);
967 : }
968 :
969 : static char *
970 20638 : echo_substitute_hook(char *newval)
971 : {
972 20638 : if (newval == NULL)
973 18664 : newval = pg_strdup("none");
974 20638 : return newval;
975 : }
976 :
977 : static bool
978 20638 : echo_hook(const char *newval)
979 : {
980 : Assert(newval != NULL); /* else substitute hook messed up */
981 20638 : if (pg_strcasecmp(newval, "queries") == 0)
982 6 : pset.echo = PSQL_ECHO_QUERIES;
983 20632 : else if (pg_strcasecmp(newval, "errors") == 0)
984 6 : pset.echo = PSQL_ECHO_ERRORS;
985 20626 : else if (pg_strcasecmp(newval, "all") == 0)
986 1948 : pset.echo = PSQL_ECHO_ALL;
987 18678 : else if (pg_strcasecmp(newval, "none") == 0)
988 18678 : pset.echo = PSQL_ECHO_NONE;
989 : else
990 : {
991 0 : PsqlVarEnumError("ECHO", newval, "none, errors, queries, all");
992 0 : return false;
993 : }
994 20638 : return true;
995 : }
996 :
997 : static bool
998 18664 : echo_hidden_hook(const char *newval)
999 : {
1000 : Assert(newval != NULL); /* else substitute hook messed up */
1001 18664 : if (pg_strcasecmp(newval, "noexec") == 0)
1002 0 : pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC;
1003 : else
1004 : {
1005 : bool on_off;
1006 :
1007 18664 : if (ParseVariableBool(newval, NULL, &on_off))
1008 18664 : pset.echo_hidden = on_off ? PSQL_ECHO_HIDDEN_ON : PSQL_ECHO_HIDDEN_OFF;
1009 : else
1010 : {
1011 0 : PsqlVarEnumError("ECHO_HIDDEN", newval, "on, off, noexec");
1012 0 : return false;
1013 : }
1014 : }
1015 18664 : return true;
1016 : }
1017 :
1018 : static bool
1019 18712 : on_error_rollback_hook(const char *newval)
1020 : {
1021 : Assert(newval != NULL); /* else substitute hook messed up */
1022 18712 : if (pg_strcasecmp(newval, "interactive") == 0)
1023 0 : pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE;
1024 : else
1025 : {
1026 : bool on_off;
1027 :
1028 18712 : if (ParseVariableBool(newval, NULL, &on_off))
1029 18706 : pset.on_error_rollback = on_off ? PSQL_ERROR_ROLLBACK_ON : PSQL_ERROR_ROLLBACK_OFF;
1030 : else
1031 : {
1032 6 : PsqlVarEnumError("ON_ERROR_ROLLBACK", newval, "on, off, interactive");
1033 6 : return false;
1034 : }
1035 : }
1036 18706 : return true;
1037 : }
1038 :
1039 : static char *
1040 18672 : comp_keyword_case_substitute_hook(char *newval)
1041 : {
1042 18672 : if (newval == NULL)
1043 18664 : newval = pg_strdup("preserve-upper");
1044 18672 : return newval;
1045 : }
1046 :
1047 : static bool
1048 18672 : comp_keyword_case_hook(const char *newval)
1049 : {
1050 : Assert(newval != NULL); /* else substitute hook messed up */
1051 18672 : if (pg_strcasecmp(newval, "preserve-upper") == 0)
1052 18666 : pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
1053 6 : else if (pg_strcasecmp(newval, "preserve-lower") == 0)
1054 2 : pset.comp_case = PSQL_COMP_CASE_PRESERVE_LOWER;
1055 4 : else if (pg_strcasecmp(newval, "upper") == 0)
1056 2 : pset.comp_case = PSQL_COMP_CASE_UPPER;
1057 2 : else if (pg_strcasecmp(newval, "lower") == 0)
1058 2 : pset.comp_case = PSQL_COMP_CASE_LOWER;
1059 : else
1060 : {
1061 0 : PsqlVarEnumError("COMP_KEYWORD_CASE", newval,
1062 : "lower, upper, preserve-lower, preserve-upper");
1063 0 : return false;
1064 : }
1065 18672 : return true;
1066 : }
1067 :
1068 : static char *
1069 18664 : histcontrol_substitute_hook(char *newval)
1070 : {
1071 18664 : if (newval == NULL)
1072 18664 : newval = pg_strdup("none");
1073 18664 : return newval;
1074 : }
1075 :
1076 : static bool
1077 18664 : histcontrol_hook(const char *newval)
1078 : {
1079 : Assert(newval != NULL); /* else substitute hook messed up */
1080 18664 : if (pg_strcasecmp(newval, "ignorespace") == 0)
1081 0 : pset.histcontrol = hctl_ignorespace;
1082 18664 : else if (pg_strcasecmp(newval, "ignoredups") == 0)
1083 0 : pset.histcontrol = hctl_ignoredups;
1084 18664 : else if (pg_strcasecmp(newval, "ignoreboth") == 0)
1085 0 : pset.histcontrol = hctl_ignoreboth;
1086 18664 : else if (pg_strcasecmp(newval, "none") == 0)
1087 18664 : pset.histcontrol = hctl_none;
1088 : else
1089 : {
1090 0 : PsqlVarEnumError("HISTCONTROL", newval,
1091 : "none, ignorespace, ignoredups, ignoreboth");
1092 0 : return false;
1093 : }
1094 18664 : return true;
1095 : }
1096 :
1097 : static bool
1098 37328 : prompt1_hook(const char *newval)
1099 : {
1100 37328 : pset.prompt1 = newval ? newval : "";
1101 37328 : return true;
1102 : }
1103 :
1104 : static bool
1105 37328 : prompt2_hook(const char *newval)
1106 : {
1107 37328 : pset.prompt2 = newval ? newval : "";
1108 37328 : return true;
1109 : }
1110 :
1111 : static bool
1112 37328 : prompt3_hook(const char *newval)
1113 : {
1114 37328 : pset.prompt3 = newval ? newval : "";
1115 37328 : return true;
1116 : }
1117 :
1118 : static char *
1119 18826 : verbosity_substitute_hook(char *newval)
1120 : {
1121 18826 : if (newval == NULL)
1122 18664 : newval = pg_strdup("default");
1123 18826 : return newval;
1124 : }
1125 :
1126 : static bool
1127 18826 : verbosity_hook(const char *newval)
1128 : {
1129 : Assert(newval != NULL); /* else substitute hook messed up */
1130 18826 : if (pg_strcasecmp(newval, "default") == 0)
1131 18734 : pset.verbosity = PQERRORS_DEFAULT;
1132 92 : else if (pg_strcasecmp(newval, "verbose") == 0)
1133 0 : pset.verbosity = PQERRORS_VERBOSE;
1134 92 : else if (pg_strcasecmp(newval, "terse") == 0)
1135 50 : pset.verbosity = PQERRORS_TERSE;
1136 42 : else if (pg_strcasecmp(newval, "sqlstate") == 0)
1137 42 : pset.verbosity = PQERRORS_SQLSTATE;
1138 : else
1139 : {
1140 0 : PsqlVarEnumError("VERBOSITY", newval, "default, verbose, terse, sqlstate");
1141 0 : return false;
1142 : }
1143 :
1144 18826 : if (pset.db)
1145 162 : PQsetErrorVerbosity(pset.db, pset.verbosity);
1146 18826 : return true;
1147 : }
1148 :
1149 : static bool
1150 37342 : show_all_results_hook(const char *newval)
1151 : {
1152 37342 : return ParseVariableBool(newval, "SHOW_ALL_RESULTS", &pset.show_all_results);
1153 : }
1154 :
1155 : static char *
1156 18718 : show_context_substitute_hook(char *newval)
1157 : {
1158 18718 : if (newval == NULL)
1159 18666 : newval = pg_strdup("errors");
1160 18718 : return newval;
1161 : }
1162 :
1163 : static bool
1164 18718 : show_context_hook(const char *newval)
1165 : {
1166 : Assert(newval != NULL); /* else substitute hook messed up */
1167 18718 : if (pg_strcasecmp(newval, "never") == 0)
1168 16 : pset.show_context = PQSHOW_CONTEXT_NEVER;
1169 18702 : else if (pg_strcasecmp(newval, "errors") == 0)
1170 18686 : pset.show_context = PQSHOW_CONTEXT_ERRORS;
1171 16 : else if (pg_strcasecmp(newval, "always") == 0)
1172 16 : pset.show_context = PQSHOW_CONTEXT_ALWAYS;
1173 : else
1174 : {
1175 0 : PsqlVarEnumError("SHOW_CONTEXT", newval, "never, errors, always");
1176 0 : return false;
1177 : }
1178 :
1179 18718 : if (pset.db)
1180 54 : PQsetErrorContextVisibility(pset.db, pset.show_context);
1181 18718 : return true;
1182 : }
1183 :
1184 : static bool
1185 20604 : hide_compression_hook(const char *newval)
1186 : {
1187 20604 : return ParseVariableBool(newval, "HIDE_TOAST_COMPRESSION",
1188 : &pset.hide_compression);
1189 : }
1190 :
1191 : static bool
1192 20604 : hide_tableam_hook(const char *newval)
1193 : {
1194 20604 : return ParseVariableBool(newval, "HIDE_TABLEAM", &pset.hide_tableam);
1195 : }
1196 :
1197 : static void
1198 18664 : EstablishVariableSpace(void)
1199 : {
1200 18664 : pset.vars = CreateVariableSpace();
1201 :
1202 18664 : SetVariableHooks(pset.vars, "AUTOCOMMIT",
1203 : bool_substitute_hook,
1204 : autocommit_hook);
1205 18664 : SetVariableHooks(pset.vars, "ON_ERROR_STOP",
1206 : bool_substitute_hook,
1207 : on_error_stop_hook);
1208 18664 : SetVariableHooks(pset.vars, "QUIET",
1209 : bool_substitute_hook,
1210 : quiet_hook);
1211 18664 : SetVariableHooks(pset.vars, "SINGLELINE",
1212 : bool_substitute_hook,
1213 : singleline_hook);
1214 18664 : SetVariableHooks(pset.vars, "SINGLESTEP",
1215 : bool_substitute_hook,
1216 : singlestep_hook);
1217 18664 : SetVariableHooks(pset.vars, "FETCH_COUNT",
1218 : fetch_count_substitute_hook,
1219 : fetch_count_hook);
1220 18664 : SetVariableHooks(pset.vars, "HISTFILE",
1221 : NULL,
1222 : histfile_hook);
1223 18664 : SetVariableHooks(pset.vars, "HISTSIZE",
1224 : histsize_substitute_hook,
1225 : histsize_hook);
1226 18664 : SetVariableHooks(pset.vars, "IGNOREEOF",
1227 : ignoreeof_substitute_hook,
1228 : ignoreeof_hook);
1229 18664 : SetVariableHooks(pset.vars, "ECHO",
1230 : echo_substitute_hook,
1231 : echo_hook);
1232 18664 : SetVariableHooks(pset.vars, "ECHO_HIDDEN",
1233 : bool_substitute_hook,
1234 : echo_hidden_hook);
1235 18664 : SetVariableHooks(pset.vars, "ON_ERROR_ROLLBACK",
1236 : bool_substitute_hook,
1237 : on_error_rollback_hook);
1238 18664 : SetVariableHooks(pset.vars, "COMP_KEYWORD_CASE",
1239 : comp_keyword_case_substitute_hook,
1240 : comp_keyword_case_hook);
1241 18664 : SetVariableHooks(pset.vars, "HISTCONTROL",
1242 : histcontrol_substitute_hook,
1243 : histcontrol_hook);
1244 18664 : SetVariableHooks(pset.vars, "PROMPT1",
1245 : NULL,
1246 : prompt1_hook);
1247 18664 : SetVariableHooks(pset.vars, "PROMPT2",
1248 : NULL,
1249 : prompt2_hook);
1250 18664 : SetVariableHooks(pset.vars, "PROMPT3",
1251 : NULL,
1252 : prompt3_hook);
1253 18664 : SetVariableHooks(pset.vars, "VERBOSITY",
1254 : verbosity_substitute_hook,
1255 : verbosity_hook);
1256 18664 : SetVariableHooks(pset.vars, "SHOW_ALL_RESULTS",
1257 : bool_substitute_hook,
1258 : show_all_results_hook);
1259 18664 : SetVariableHooks(pset.vars, "SHOW_CONTEXT",
1260 : show_context_substitute_hook,
1261 : show_context_hook);
1262 18664 : SetVariableHooks(pset.vars, "HIDE_TOAST_COMPRESSION",
1263 : bool_substitute_hook,
1264 : hide_compression_hook);
1265 18664 : SetVariableHooks(pset.vars, "HIDE_TABLEAM",
1266 : bool_substitute_hook,
1267 : hide_tableam_hook);
1268 18664 : }
|