]> granicus.if.org Git - postgresql/blob - src/bin/psql/startup.c
d7ef12a315ac533b6b3652fb0336c4a25ea10c88
[postgresql] / src / bin / psql / startup.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2004, PostgreSQL Global Development Group
5  *
6  * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.105 2004/11/01 19:21:50 momjian Exp $
7  */
8 #include "postgres_fe.h"
9
10 #include <sys/types.h>
11
12 #ifndef WIN32
13 #include <unistd.h>
14 #else                                                   /* WIN32 */
15 #include <io.h>
16 #include <win32.h>
17 #endif   /* WIN32 */
18
19 #include "getopt_long.h"
20
21 #ifndef HAVE_OPTRESET
22 int                     optreset;
23 #endif
24
25 #include <locale.h>
26
27 #include "libpq-fe.h"
28
29 #include "command.h"
30 #include "common.h"
31 #include "describe.h"
32 #include "help.h"
33 #include "input.h"
34 #include "mainloop.h"
35 #include "print.h"
36 #include "settings.h"
37 #include "variables.h"
38
39 #include "mb/pg_wchar.h"
40
41 /*
42  * Global psql options
43  */
44 PsqlSettings pset;
45
46 #define SYSPSQLRC       "psqlrc"
47 #define PSQLRC          ".psqlrc"
48
49 /*
50  * Structures to pass information between the option parsing routine
51  * and the main function
52  */
53 enum _actions
54 {
55         ACT_NOTHING = 0,
56         ACT_SINGLE_SLASH,
57         ACT_LIST_DB,
58         ACT_SINGLE_QUERY,
59         ACT_FILE
60 };
61
62 struct adhoc_opts
63 {
64         char       *dbname;
65         char       *host;
66         char       *port;
67         char       *username;
68         enum _actions action;
69         char       *action_string;
70         bool            no_readline;
71         bool            no_psqlrc;
72 };
73
74 static void parse_psql_options(int argc, char *argv[],
75                                    struct adhoc_opts * options);
76 static void process_psqlrc(char *argv0);
77 static void process_psqlrc_file(char *filename);
78 static void showVersion(void);
79
80 #ifdef USE_SSL
81 static void printSSLInfo(void);
82 #endif
83
84 #ifdef WIN32
85 static void
86                         checkWin32Codepage(void);
87 #endif
88
89 /*
90  *
91  * main
92  *
93  */
94 int
95 main(int argc, char *argv[])
96 {
97         struct adhoc_opts options;
98         int                     successResult;
99
100         char       *username = NULL;
101         char       *password = NULL;
102         bool            need_pass;
103
104         set_pglocale_pgservice(argv[0], "psql");
105
106         pset.progname = get_progname(argv[0]);
107
108         if (argc > 1)
109         {
110                 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
111                 {
112                         usage();
113                         exit(EXIT_SUCCESS);
114                 }
115                 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
116                 {
117                         showVersion();
118                         exit(EXIT_SUCCESS);
119                 }
120         }
121
122 #ifdef WIN32
123         setvbuf(stderr, NULL, _IONBF, 0);
124         setup_win32_locks();
125 #endif
126         pset.cur_cmd_source = stdin;
127         pset.cur_cmd_interactive = false;
128         pset.encoding = PQenv2encoding();
129
130         pset.vars = CreateVariableSpace();
131         if (!pset.vars)
132         {
133                 fprintf(stderr, gettext("%s: out of memory\n"), pset.progname);
134                 exit(EXIT_FAILURE);
135         }
136         pset.popt.topt.format = PRINT_ALIGNED;
137         pset.queryFout = stdout;
138         pset.popt.topt.border = 1;
139         pset.popt.topt.pager = 1;
140         pset.popt.default_footer = true;
141
142         SetVariable(pset.vars, "VERSION", PG_VERSION_STR);
143
144         /* Default values for variables */
145         SetVariableBool(pset.vars, "AUTOCOMMIT");
146         SetVariable(pset.vars, "VERBOSITY", "default");
147         SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1);
148         SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
149         SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
150
151         pset.verbosity = PQERRORS_DEFAULT;
152
153         pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
154
155         /* This is obsolete and should be removed sometime. */
156 #ifdef PSQL_ALWAYS_GET_PASSWORDS
157         pset.getPassword = true;
158 #else
159         pset.getPassword = false;
160 #endif
161
162 #ifndef HAVE_UNIX_SOCKETS
163         /* default to localhost on platforms without unix sockets */
164         options.host = "localhost";
165 #endif
166
167         parse_psql_options(argc, argv, &options);
168
169         if (!pset.popt.topt.fieldSep)
170                 pset.popt.topt.fieldSep = pg_strdup(DEFAULT_FIELD_SEP);
171         if (!pset.popt.topt.recordSep)
172                 pset.popt.topt.recordSep = pg_strdup(DEFAULT_RECORD_SEP);
173
174         if (options.username)
175         {
176                 /*
177                  * The \001 is a hack to support the deprecated -u option which
178                  * issues a username prompt. The recommended option is -U followed
179                  * by the name on the command line.
180                  */
181                 if (strcmp(options.username, "\001") == 0)
182                         username = simple_prompt("User name: ", 100, true);
183                 else
184                         username = pg_strdup(options.username);
185         }
186
187         if (pset.getPassword)
188                 password = simple_prompt("Password: ", 100, false);
189
190         /* loop until we have a password if requested by backend */
191         do
192         {
193                 need_pass = false;
194                 pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL,
195                         options.action == ACT_LIST_DB ? "template1" : options.dbname,
196                                                            username, password);
197
198                 if (PQstatus(pset.db) == CONNECTION_BAD &&
199                         strcmp(PQerrorMessage(pset.db), PQnoPasswordSupplied) == 0 &&
200                         !feof(stdin))
201                 {
202                         PQfinish(pset.db);
203                         need_pass = true;
204                         free(password);
205                         password = NULL;
206                         password = simple_prompt("Password: ", 100, false);
207                 }
208         } while (need_pass);
209
210         free(username);
211         free(password);
212
213         if (PQstatus(pset.db) == CONNECTION_BAD)
214         {
215                 fprintf(stderr, "%s: %s", pset.progname, PQerrorMessage(pset.db));
216                 PQfinish(pset.db);
217                 exit(EXIT_BADCONN);
218         }
219
220         PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
221
222         SyncVariables();
223
224         /* Grab the backend server version */
225         pset.sversion = PQserverVersion(pset.db);
226
227         if (options.action == ACT_LIST_DB)
228         {
229                 int                     success = listAllDbs(false);
230
231                 PQfinish(pset.db);
232                 exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
233         }
234
235         /*
236          * Now find something to do
237          */
238
239         /*
240          * process file given by -f
241          */
242         if (options.action == ACT_FILE && strcmp(options.action_string, "-") != 0)
243         {
244                 if (!options.no_psqlrc)
245                         process_psqlrc(argv[0]);
246
247                 successResult = process_file(options.action_string);
248         }
249
250         /*
251          * process slash command if one was given to -c
252          */
253         else if (options.action == ACT_SINGLE_SLASH)
254         {
255                 PsqlScanState scan_state;
256
257                 if (VariableEquals(pset.vars, "ECHO", "all"))
258                         puts(options.action_string);
259
260                 scan_state = psql_scan_create();
261                 psql_scan_setup(scan_state,
262                                                 options.action_string,
263                                                 strlen(options.action_string));
264
265                 successResult = HandleSlashCmds(scan_state, NULL) != CMD_ERROR
266                         ? EXIT_SUCCESS : EXIT_FAILURE;
267
268                 psql_scan_destroy(scan_state);
269         }
270
271         /*
272          * If the query given to -c was a normal one, send it
273          */
274         else if (options.action == ACT_SINGLE_QUERY)
275         {
276                 if (VariableEquals(pset.vars, "ECHO", "all"))
277                         puts(options.action_string);
278
279                 successResult = SendQuery(options.action_string)
280                         ? EXIT_SUCCESS : EXIT_FAILURE;
281         }
282
283         /*
284          * or otherwise enter interactive main loop
285          */
286         else
287         {
288                 if (!options.no_psqlrc)
289                         process_psqlrc(argv[0]);
290
291                 if (!QUIET() && !pset.notty)
292                 {
293                         printf(gettext("Welcome to %s %s, the PostgreSQL interactive terminal.\n\n"
294                                                    "Type:  \\copyright for distribution terms\n"
295                                                    "       \\h for help with SQL commands\n"
296                                                    "       \\? for help with psql commands\n"
297                           "       \\g or terminate with semicolon to execute query\n"
298                                                    "       \\q to quit\n\n"),
299                                    pset.progname, PG_VERSION);
300 #ifdef USE_SSL
301                         printSSLInfo();
302 #endif
303 #ifdef WIN32
304                         checkWin32Codepage();
305 #endif
306                 }
307
308                 if (!pset.notty)
309                         initializeInput(options.no_readline ? 0 : 1);
310                 if (options.action_string)              /* -f - was used */
311                         pset.inputfile = "<stdin>";
312
313                 successResult = MainLoop(stdin);
314         }
315
316         /* clean up */
317         PQfinish(pset.db);
318         setQFout(NULL);
319
320         return successResult;
321 }
322
323
324
325 /*
326  * Parse command line options
327  */
328
329 static void
330 parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
331 {
332         static struct option long_options[] =
333         {
334                 {"echo-all", no_argument, NULL, 'a'},
335                 {"no-align", no_argument, NULL, 'A'},
336                 {"command", required_argument, NULL, 'c'},
337                 {"dbname", required_argument, NULL, 'd'},
338                 {"echo-queries", no_argument, NULL, 'e'},
339                 {"echo-hidden", no_argument, NULL, 'E'},
340                 {"file", required_argument, NULL, 'f'},
341                 {"field-separator", required_argument, NULL, 'F'},
342                 {"host", required_argument, NULL, 'h'},
343                 {"html", no_argument, NULL, 'H'},
344                 {"list", no_argument, NULL, 'l'},
345                 {"no-readline", no_argument, NULL, 'n'},
346                 {"output", required_argument, NULL, 'o'},
347                 {"port", required_argument, NULL, 'p'},
348                 {"pset", required_argument, NULL, 'P'},
349                 {"quiet", no_argument, NULL, 'q'},
350                 {"record-separator", required_argument, NULL, 'R'},
351                 {"single-step", no_argument, NULL, 's'},
352                 {"single-line", no_argument, NULL, 'S'},
353                 {"tuples-only", no_argument, NULL, 't'},
354                 {"table-attr", required_argument, NULL, 'T'},
355                 {"username", required_argument, NULL, 'U'},
356                 {"set", required_argument, NULL, 'v'},
357                 {"variable", required_argument, NULL, 'v'},
358                 {"version", no_argument, NULL, 'V'},
359                 {"password", no_argument, NULL, 'W'},
360                 {"expanded", no_argument, NULL, 'x'},
361                 {"no-psqlrc", no_argument, NULL, 'X'},
362                 {"help", no_argument, NULL, '?'},
363                 {NULL, 0, NULL, 0}
364         };
365
366         int                     optindex;
367         extern char *optarg;
368         extern int      optind;
369         int                     c;
370         bool            used_old_u_option = false;
371
372         memset(options, 0, sizeof *options);
373
374         while ((c = getopt_long(argc, argv, "aAc:d:eEf:F:h:Hlno:p:P:qR:sStT:uU:v:VWxX?",
375                                                         long_options, &optindex)) != -1)
376         {
377                 switch (c)
378                 {
379                         case 'a':
380                                 SetVariable(pset.vars, "ECHO", "all");
381                                 break;
382                         case 'A':
383                                 pset.popt.topt.format = PRINT_UNALIGNED;
384                                 break;
385                         case 'c':
386                                 options->action_string = optarg;
387                                 if (optarg[0] == '\\')
388                                 {
389                                         options->action = ACT_SINGLE_SLASH;
390                                         options->action_string++;
391                                 }
392                                 else
393                                         options->action = ACT_SINGLE_QUERY;
394                                 break;
395                         case 'd':
396                                 options->dbname = optarg;
397                                 break;
398                         case 'e':
399                                 SetVariable(pset.vars, "ECHO", "queries");
400                                 break;
401                         case 'E':
402                                 SetVariableBool(pset.vars, "ECHO_HIDDEN");
403                                 break;
404                         case 'f':
405                                 options->action = ACT_FILE;
406                                 options->action_string = optarg;
407                                 break;
408                         case 'F':
409                                 pset.popt.topt.fieldSep = pg_strdup(optarg);
410                                 break;
411                         case 'h':
412                                 options->host = optarg;
413                                 break;
414                         case 'H':
415                                 pset.popt.topt.format = PRINT_HTML;
416                                 break;
417                         case 'l':
418                                 options->action = ACT_LIST_DB;
419                                 break;
420                         case 'n':
421                                 options->no_readline = true;
422                                 break;
423                         case 'o':
424                                 setQFout(optarg);
425                                 break;
426                         case 'p':
427                                 options->port = optarg;
428                                 break;
429                         case 'P':
430                                 {
431                                         char       *value;
432                                         char       *equal_loc;
433                                         bool            result;
434
435                                         value = pg_strdup(optarg);
436                                         equal_loc = strchr(value, '=');
437                                         if (!equal_loc)
438                                                 result = do_pset(value, NULL, &pset.popt, true);
439                                         else
440                                         {
441                                                 *equal_loc = '\0';
442                                                 result = do_pset(value, equal_loc + 1, &pset.popt, true);
443                                         }
444
445                                         if (!result)
446                                         {
447                                                 fprintf(stderr, gettext("%s: couldn't set printing parameter \"%s\"\n"), pset.progname, value);
448                                                 exit(EXIT_FAILURE);
449                                         }
450
451                                         free(value);
452                                         break;
453                                 }
454                         case 'q':
455                                 SetVariableBool(pset.vars, "QUIET");
456                                 break;
457                         case 'R':
458                                 pset.popt.topt.recordSep = pg_strdup(optarg);
459                                 break;
460                         case 's':
461                                 SetVariableBool(pset.vars, "SINGLESTEP");
462                                 break;
463                         case 'S':
464                                 SetVariableBool(pset.vars, "SINGLELINE");
465                                 break;
466                         case 't':
467                                 pset.popt.topt.tuples_only = true;
468                                 break;
469                         case 'T':
470                                 pset.popt.topt.tableAttr = pg_strdup(optarg);
471                                 break;
472                         case 'u':
473                                 pset.getPassword = true;
474                                 options->username = "\001";             /* hopefully nobody has
475                                                                                                  * that username */
476                                 /* this option is out */
477                                 used_old_u_option = true;
478                                 break;
479                         case 'U':
480                                 options->username = optarg;
481                                 break;
482                         case 'v':
483                                 {
484                                         char       *value;
485                                         char       *equal_loc;
486
487                                         value = pg_strdup(optarg);
488                                         equal_loc = strchr(value, '=');
489                                         if (!equal_loc)
490                                         {
491                                                 if (!DeleteVariable(pset.vars, value))
492                                                 {
493                                                         fprintf(stderr, gettext("%s: could not delete variable \"%s\"\n"),
494                                                                         pset.progname, value);
495                                                         exit(EXIT_FAILURE);
496                                                 }
497                                         }
498                                         else
499                                         {
500                                                 *equal_loc = '\0';
501                                                 if (!SetVariable(pset.vars, value, equal_loc + 1))
502                                                 {
503                                                         fprintf(stderr, gettext("%s: could not set variable \"%s\"\n"),
504                                                                         pset.progname, value);
505                                                         exit(EXIT_FAILURE);
506                                                 }
507                                         }
508
509                                         free(value);
510                                         break;
511                                 }
512                         case 'V':
513                                 showVersion();
514                                 exit(EXIT_SUCCESS);
515                         case 'W':
516                                 pset.getPassword = true;
517                                 break;
518                         case 'x':
519                                 pset.popt.topt.expanded = true;
520                                 break;
521                         case 'X':
522                                 options->no_psqlrc = true;
523                                 break;
524                         case '?':
525                                 /* Actual help option given */
526                                 if (strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0)
527                                 {
528                                         usage();
529                                         exit(EXIT_SUCCESS);
530                                 }
531                                 /* unknown option reported by getopt */
532                                 else
533                                 {
534                                         fprintf(stderr, gettext("Try \"%s --help\" for more information.\n"),
535                                                         pset.progname);
536                                         exit(EXIT_FAILURE);
537                                 }
538                                 break;
539                         default:
540                                 fprintf(stderr, gettext("Try \"%s --help\" for more information.\n"),
541                                                 pset.progname);
542                                 exit(EXIT_FAILURE);
543                                 break;
544                 }
545         }
546
547         /*
548          * if we still have arguments, use it as the database name and
549          * username
550          */
551         while (argc - optind >= 1)
552         {
553                 if (!options->dbname)
554                         options->dbname = argv[optind];
555                 else if (!options->username)
556                         options->username = argv[optind];
557                 else if (!QUIET())
558                         fprintf(stderr, gettext("%s: warning: extra command-line argument \"%s\" ignored\n"),
559                                         pset.progname, argv[optind]);
560
561                 optind++;
562         }
563
564         if (used_old_u_option && !QUIET())
565                 fprintf(stderr, gettext("%s: Warning: The -u option is deprecated. Use -U.\n"), pset.progname);
566
567 }
568
569
570 /*
571  * Load .psqlrc file, if found.
572  */
573 static void
574 process_psqlrc(char *argv0)
575 {
576         char       *psqlrc;
577         char            home[MAXPGPATH];
578         char            global_file[MAXPGPATH];
579         char            my_exec_path[MAXPGPATH];
580         char            etc_path[MAXPGPATH];
581
582         find_my_exec(argv0, my_exec_path);
583         get_etc_path(my_exec_path, etc_path);
584
585         snprintf(global_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC);
586         process_psqlrc_file(global_file);
587
588         if (get_home_path(home))
589         {
590                 psqlrc = pg_malloc(strlen(home) + 1 + strlen(PSQLRC) + 1);
591                 sprintf(psqlrc, "%s/%s", home, PSQLRC);
592                 process_psqlrc_file(psqlrc);
593                 free(psqlrc);
594         }
595 }
596
597
598
599 static void
600 process_psqlrc_file(char *filename)
601 {
602         char       *psqlrc;
603
604 #if defined(WIN32) && (!defined(__MINGW32__))
605 #define R_OK 4
606 #endif
607
608         psqlrc = pg_malloc(strlen(filename) + 1 + strlen(PG_VERSION) + 1);
609         sprintf(psqlrc, "%s-%s", filename, PG_VERSION);
610
611         if (access(psqlrc, R_OK) == 0)
612                 process_file(psqlrc);
613         else if (access(filename, R_OK) == 0)
614                 process_file(filename);
615         free(psqlrc);
616 }
617
618
619
620 /* showVersion
621  *
622  * This output format is intended to match GNU standards.
623  */
624 static void
625 showVersion(void)
626 {
627         puts("psql (PostgreSQL) " PG_VERSION);
628
629 #if defined(USE_READLINE)
630         puts(gettext("contains support for command-line editing"));
631 #endif
632 }
633
634
635
636 /*
637  * printSSLInfo
638  *
639  * Prints information about the current SSL connection, if SSL is in use
640  */
641 #ifdef USE_SSL
642 static void
643 printSSLInfo(void)
644 {
645         int                     sslbits = -1;
646         SSL                *ssl;
647
648         ssl = PQgetssl(pset.db);
649         if (!ssl)
650                 return;                                 /* no SSL */
651
652         SSL_get_cipher_bits(ssl, &sslbits);
653         printf(gettext("SSL connection (cipher: %s, bits: %i)\n\n"),
654                    SSL_get_cipher(ssl), sslbits);
655 }
656 #endif
657
658
659
660 /*
661  * checkWin32Codepage
662  *
663  * Prints a warning when win32 console codepage differs from Windows codepage
664  */
665 #ifdef WIN32
666 static void
667 checkWin32Codepage(void)
668 {
669         unsigned int wincp,
670                                 concp;
671
672         wincp = GetACP();
673         concp = GetConsoleCP();
674         if (wincp != concp)
675         {
676                 printf("Warning: Console codepage (%u) differs from windows codepage (%u)\n"
677                            "         8-bit characters will not work correctly. See PostgreSQL\n"
678                            "         documentation \"Installation on Windows\" for details.\n\n",
679                            concp, wincp);
680         }
681 }
682
683 #endif