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