]> granicus.if.org Git - postgresql/blob - src/bin/psql/common.c
Make psql \timing show three digits.
[postgresql] / src / bin / psql / common.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2003, PostgreSQL Global Development Group
5  *
6  * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.75 2003/10/05 22:36:00 momjian Exp $
7  */
8 #include "postgres_fe.h"
9 #include "common.h"
10
11 #include <ctype.h>
12 #ifndef HAVE_STRDUP
13 #include <strdup.h>
14 #endif
15 #include <signal.h>
16 #ifndef WIN32
17 #include <sys/time.h>
18 #include <unistd.h>                             /* for write() */
19 #include <setjmp.h>
20 #else
21 #include <io.h>                                 /* for _write() */
22 #include <win32.h>
23 #include <sys/timeb.h>                  /* for _ftime() */
24 #endif
25
26 #include "libpq-fe.h"
27 #include "pqsignal.h"
28
29 #include "settings.h"
30 #include "variables.h"
31 #include "command.h"
32 #include "copy.h"
33 #include "prompt.h"
34 #include "print.h"
35 #include "mainloop.h"
36 #include "mb/pg_wchar.h"
37
38
39 /* Workarounds for Windows */
40 /* Probably to be moved up the source tree in the future, perhaps to be replaced by
41  * more specific checks like configure-style HAVE_GETTIMEOFDAY macros.
42  */
43 #ifndef WIN32
44
45 typedef struct timeval TimevalStruct;
46
47 #define GETTIMEOFDAY(T) gettimeofday(T, NULL)
48 #define DIFF_MSEC(T, U) ((((T)->tv_sec - (U)->tv_sec) * 1000000.0 + (T)->tv_usec - (U)->tv_usec) / 1000.0)
49
50 #else
51
52 typedef struct _timeb TimevalStruct;
53
54 #define GETTIMEOFDAY(T) _ftime(T)
55 #define DIFF_MSEC(T, U) ((((T)->time - (U)->time) * 1000.0 + (T)->millitm - (U)->millitm))
56 #endif
57
58 extern bool prompt_state;
59
60
61 static bool is_transact_command(const char *query);
62
63
64 /*
65  * "Safe" wrapper around strdup()
66  */
67 char *
68 xstrdup(const char *string)
69 {
70         char       *tmp;
71
72         if (!string)
73         {
74                 fprintf(stderr, gettext("%s: xstrdup: cannot duplicate null pointer (internal error)\n"),
75                                 pset.progname);
76                 exit(EXIT_FAILURE);
77         }
78         tmp = strdup(string);
79         if (!tmp)
80         {
81                 psql_error("out of memory\n");
82                 exit(EXIT_FAILURE);
83         }
84         return tmp;
85 }
86
87
88
89 /*
90  * setQFout
91  * -- handler for -o command line option and \o command
92  *
93  * Tries to open file fname (or pipe if fname starts with '|')
94  * and stores the file handle in pset)
95  * Upon failure, sets stdout and returns false.
96  */
97 bool
98 setQFout(const char *fname)
99 {
100         bool            status = true;
101
102         /* Close old file/pipe */
103         if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
104         {
105                 if (pset.queryFoutPipe)
106                         pclose(pset.queryFout);
107                 else
108                         fclose(pset.queryFout);
109         }
110
111         /* If no filename, set stdout */
112         if (!fname || fname[0] == '\0')
113         {
114                 pset.queryFout = stdout;
115                 pset.queryFoutPipe = false;
116         }
117         else if (*fname == '|')
118         {
119                 pset.queryFout = popen(fname + 1, "w");
120                 pset.queryFoutPipe = true;
121         }
122         else
123         {
124                 pset.queryFout = fopen(fname, "w");
125                 pset.queryFoutPipe = false;
126         }
127
128         if (!(pset.queryFout))
129         {
130                 psql_error("%s: %s\n", fname, strerror(errno));
131                 pset.queryFout = stdout;
132                 pset.queryFoutPipe = false;
133                 status = false;
134         }
135
136         /* Direct signals */
137 #ifndef WIN32
138         pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL);
139 #endif
140
141         return status;
142 }
143
144
145
146 /*
147  * Error reporting for scripts. Errors should look like
148  *       psql:filename:lineno: message
149  *
150  */
151 void
152 psql_error(const char *fmt,...)
153 {
154         va_list         ap;
155
156         fflush(stdout);
157         if (pset.queryFout != stdout)
158                 fflush(pset.queryFout);
159
160         if (pset.inputfile)
161                 fprintf(stderr, "%s:%s:%u: ", pset.progname, pset.inputfile, pset.lineno);
162         va_start(ap, fmt);
163         vfprintf(stderr, gettext(fmt), ap);
164         va_end(ap);
165 }
166
167
168
169 /*
170  * for backend Notice messages (INFO, WARNING, etc)
171  */
172 void
173 NoticeProcessor(void *arg, const char *message)
174 {
175         (void) arg;                                     /* not used */
176         psql_error("%s", message);
177 }
178
179
180
181 /*
182  * Code to support query cancellation
183  *
184  * Before we start a query, we enable a SIGINT signal catcher that sends a
185  * cancel request to the backend. Note that sending the cancel directly from
186  * the signal handler is safe because PQrequestCancel() is written to make it
187  * so. We use write() to print to stdout because it's better to use simple
188  * facilities in a signal handler.
189  */
190 static PGconn *volatile cancelConn = NULL;
191
192 volatile bool cancel_pressed = false;
193
194
195 #ifndef WIN32
196
197 #define write_stderr(String) write(fileno(stderr), String, strlen(String))
198
199 void
200 handle_sigint(SIGNAL_ARGS)
201 {
202         int                     save_errno = errno;
203
204         /* Don't muck around if prompting for a password. */
205         if (prompt_state)
206                 return;
207
208         if (cancelConn == NULL)
209                 siglongjmp(main_loop_jmp, 1);
210
211         cancel_pressed = true;
212
213         if (PQrequestCancel(cancelConn))
214                 write_stderr("Cancel request sent\n");
215         else
216         {
217                 write_stderr("Could not send cancel request: ");
218                 write_stderr(PQerrorMessage(cancelConn));
219         }
220         errno = save_errno;                     /* just in case the write changed it */
221 }
222 #endif   /* not WIN32 */
223
224
225
226 /* ConnectionUp
227  *
228  * Returns whether our backend connection is still there.
229  */
230 static bool
231 ConnectionUp()
232 {
233         return PQstatus(pset.db) != CONNECTION_BAD;
234 }
235
236
237
238 /* CheckConnection
239  *
240  * Verify that we still have a good connection to the backend, and if not,
241  * see if it can be restored.
242  *
243  * Returns true if either the connection was still there, or it could be
244  * restored successfully; false otherwise.      If, however, there was no
245  * connection and the session is non-interactive, this will exit the program
246  * with a code of EXIT_BADCONN.
247  */
248 static bool
249 CheckConnection(void)
250 {
251         bool            OK;
252
253         OK = ConnectionUp();
254         if (!OK)
255         {
256                 if (!pset.cur_cmd_interactive)
257                 {
258                         psql_error("connection to server was lost\n");
259                         exit(EXIT_BADCONN);
260                 }
261
262                 fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr);
263                 PQreset(pset.db);
264                 OK = ConnectionUp();
265                 if (!OK)
266                 {
267                         fputs(gettext("Failed.\n"), stderr);
268                         PQfinish(pset.db);
269                         pset.db = NULL;
270                         ResetCancelConn();
271                         UnsyncVariables();
272                 }
273                 else
274                         fputs(gettext("Succeeded.\n"), stderr);
275         }
276
277         return OK;
278 }
279
280
281
282 /*
283  * SetCancelConn
284  *
285  * Set cancelConn to point to the current database connection.
286  */
287 static void
288 SetCancelConn(void)
289 {
290         cancelConn = pset.db;
291 }
292
293
294 /*
295  * ResetCancelConn
296  *
297  * Set cancelConn to NULL.      I don't know what this means exactly, but it saves
298  * having to export the variable.
299  */
300 void
301 ResetCancelConn(void)
302 {
303         cancelConn = NULL;
304 }
305
306
307 /*
308  * AcceptResult
309  *
310  * Checks whether a result is valid, giving an error message if necessary;
311  * resets cancelConn as needed, and ensures that the connection to the backend
312  * is still up.
313  *
314  * Returns true for valid result, false for error state.
315  */
316 static bool
317 AcceptResult(const PGresult *result)
318 {
319         bool            OK = true;
320
321         ResetCancelConn();
322
323         if (!result)
324                 OK = false;
325         else
326                 switch (PQresultStatus(result))
327                 {
328                         case PGRES_COMMAND_OK:
329                         case PGRES_TUPLES_OK:
330                         case PGRES_COPY_IN:
331                                 /* Fine, do nothing */
332                                 break;
333
334                         case PGRES_COPY_OUT:
335                                 /* keep cancel connection for copy out state */
336                                 SetCancelConn();
337                                 break;
338
339                         default:
340                                 OK = false;
341                                 break;
342                 }
343
344         if (!OK)
345         {
346                 psql_error("%s", PQerrorMessage(pset.db));
347                 CheckConnection();
348         }
349
350         return OK;
351 }
352
353
354
355 /*
356  * PSQLexec
357  *
358  * This is the way to send "backdoor" queries (those not directly entered
359  * by the user). It is subject to -E but not -e.
360  *
361  * In autocommit-off mode, a new transaction block is started if start_xact
362  * is true; nothing special is done when start_xact is false.  Typically,
363  * start_xact = false is used for SELECTs and explicit BEGIN/COMMIT commands.
364  *
365  * Note: we don't bother to check PQclientEncoding; it is assumed that no
366  * caller uses this path to issue "SET CLIENT_ENCODING".
367  */
368 PGresult *
369 PSQLexec(const char *query, bool start_xact)
370 {
371         PGresult   *res;
372         int                     echo_hidden;
373
374         if (!pset.db)
375         {
376                 psql_error("You are currently not connected to a database.\n");
377                 return NULL;
378         }
379
380         echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL);
381         if (echo_hidden != VAR_NOTSET)
382         {
383                 printf("********* QUERY **********\n"
384                            "%s\n"
385                            "**************************\n\n", query);
386                 fflush(stdout);
387
388                 if (echo_hidden == 1)   /* noexec? */
389                         return NULL;
390         }
391
392         SetCancelConn();
393
394         if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
395                 !GetVariableBool(pset.vars, "AUTOCOMMIT"))
396         {
397                 res = PQexec(pset.db, "BEGIN");
398                 if (PQresultStatus(res) != PGRES_COMMAND_OK)
399                 {
400                         psql_error("%s", PQerrorMessage(pset.db));
401                         PQclear(res);
402                         ResetCancelConn();
403                         return NULL;
404                 }
405                 PQclear(res);
406         }
407
408         res = PQexec(pset.db, query);
409
410         if (!AcceptResult(res) && res)
411         {
412                 PQclear(res);
413                 res = NULL;
414         }
415
416         return res;
417 }
418
419
420
421 /*
422  * PrintNotifications: check for asynchronous notifications, and print them out
423  */
424 static void
425 PrintNotifications(void)
426 {
427         PGnotify   *notify;
428
429         while ((notify = PQnotifies(pset.db)))
430         {
431                 fprintf(pset.queryFout, gettext("Asynchronous notification \"%s\" received from server process with PID %d.\n"),
432                                 notify->relname, notify->be_pid);
433                 fflush(pset.queryFout);
434                 PQfreemem(notify);
435         }
436 }
437
438
439 /*
440  * PrintQueryTuples: assuming query result is OK, print its tuples
441  *
442  * Returns true if successful, false otherwise.
443  */
444 static bool
445 PrintQueryTuples(const PGresult *results)
446 {
447         /* write output to \g argument, if any */
448         if (pset.gfname)
449         {
450                 FILE       *queryFout_copy = pset.queryFout;
451                 bool            queryFoutPipe_copy = pset.queryFoutPipe;
452
453                 pset.queryFout = stdout;        /* so it doesn't get closed */
454
455                 /* open file/pipe */
456                 if (!setQFout(pset.gfname))
457                 {
458                         pset.queryFout = queryFout_copy;
459                         pset.queryFoutPipe = queryFoutPipe_copy;
460                         return false;
461                 }
462
463                 printQuery(results, &pset.popt, pset.queryFout);
464
465                 /* close file/pipe, restore old setting */
466                 setQFout(NULL);
467
468                 pset.queryFout = queryFout_copy;
469                 pset.queryFoutPipe = queryFoutPipe_copy;
470
471                 free(pset.gfname);
472                 pset.gfname = NULL;
473         }
474         else
475                 printQuery(results, &pset.popt, pset.queryFout);
476
477         return true;
478 }
479
480
481
482 /*
483  * PrintQueryResults: analyze query results and print them out
484  *
485  * Note: Utility function for use by SendQuery() only.
486  *
487  * Returns true if the query executed successfully, false otherwise.
488  */
489 static bool
490 PrintQueryResults(PGresult *results,
491                                   const TimevalStruct *before,
492                                   const TimevalStruct *after)
493 {
494         bool            success = false;
495
496         if (!results)
497                 return false;
498
499         switch (PQresultStatus(results))
500         {
501                 case PGRES_TUPLES_OK:
502                         success = PrintQueryTuples(results);
503                         break;
504                 case PGRES_EMPTY_QUERY:
505                         success = true;
506                         break;
507                 case PGRES_COMMAND_OK:
508                         {
509                                 char            buf[10];
510
511                                 success = true;
512                                 sprintf(buf, "%u", (unsigned int) PQoidValue(results));
513                                 if (!QUIET())
514                                 {
515                                         if (pset.popt.topt.format == PRINT_HTML)
516                                         {
517                                                 fputs("<p>", pset.queryFout);
518                                                 html_escaped_print(PQcmdStatus(results),
519                                                                                    pset.queryFout);
520                                                 fputs("</p>\n", pset.queryFout);
521                                         }
522                                         else
523                                                 fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
524                                 }
525                                 SetVariable(pset.vars, "LASTOID", buf);
526                                 break;
527                         }
528                 case PGRES_COPY_OUT:
529                         success = handleCopyOut(pset.db, pset.queryFout);
530                         break;
531
532                 case PGRES_COPY_IN:
533                         if (pset.cur_cmd_interactive && !QUIET())
534                                 puts(gettext("Enter data to be copied followed by a newline.\n"
535                                                          "End with a backslash and a period on a line by itself."));
536
537                         success = handleCopyIn(pset.db, pset.cur_cmd_source,
538                           pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
539                         break;
540
541                 default:
542                         break;
543         }
544
545         fflush(pset.queryFout);
546
547         /* may need this to recover from conn loss during COPY */
548         if (!CheckConnection())
549                 return false;
550
551         /* Possible microtiming output */
552         if (pset.timing && success)
553                 printf(gettext("Time: %.3f ms\n"), DIFF_MSEC(after, before));
554
555         return success;
556 }
557
558
559
560 /*
561  * SendQuery: send the query string to the backend
562  * (and print out results)
563  *
564  * Note: This is the "front door" way to send a query. That is, use it to
565  * send queries actually entered by the user. These queries will be subject to
566  * single step mode.
567  * To send "back door" queries (generated by slash commands, etc.) in a
568  * controlled way, use PSQLexec().
569  *
570  * Returns true if the query executed successfully, false otherwise.
571  */
572 bool
573 SendQuery(const char *query)
574 {
575         PGresult   *results;
576         TimevalStruct before,
577                                 after;
578         bool            OK;
579
580         if (!pset.db)
581         {
582                 psql_error("You are currently not connected to a database.\n");
583                 return false;
584         }
585
586         if (GetVariableBool(pset.vars, "SINGLESTEP"))
587         {
588                 char            buf[3];
589
590                 printf(gettext("***(Single step mode: verify command)*******************************************\n"
591                                            "%s\n"
592                                            "***(press return to proceed or enter x and return to cancel)********************\n"),
593                            query);
594                 fflush(stdout);
595                 if (fgets(buf, sizeof(buf), stdin) != NULL)
596                         if (buf[0] == 'x')
597                                 return false;
598         }
599         else if (VariableEquals(pset.vars, "ECHO", "queries"))
600         {
601                 puts(query);
602                 fflush(stdout);
603         }
604
605         SetCancelConn();
606
607         if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
608                 !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
609                 !is_transact_command(query))
610         {
611                 results = PQexec(pset.db, "BEGIN");
612                 if (PQresultStatus(results) != PGRES_COMMAND_OK)
613                 {
614                         psql_error("%s", PQerrorMessage(pset.db));
615                         PQclear(results);
616                         ResetCancelConn();
617                         return false;
618                 }
619                 PQclear(results);
620         }
621
622         if (pset.timing)
623                 GETTIMEOFDAY(&before);
624         results = PQexec(pset.db, query);
625         if (pset.timing)
626                 GETTIMEOFDAY(&after);
627
628         OK = (AcceptResult(results) && PrintQueryResults(results, &before, &after));
629         PQclear(results);
630
631         /* check for events that may occur during query execution */
632
633         if (pset.encoding != PQclientEncoding(pset.db) &&
634                 PQclientEncoding(pset.db) >= 0)
635         {
636                 /* track effects of SET CLIENT_ENCODING */
637                 pset.encoding = PQclientEncoding(pset.db);
638                 pset.popt.topt.encoding = pset.encoding;
639                 SetVariable(pset.vars, "ENCODING",
640                                         pg_encoding_to_char(pset.encoding));
641         }
642
643         PrintNotifications();
644
645         return OK;
646 }
647
648 /*
649  * check whether a query string begins with BEGIN/COMMIT/ROLLBACK/START XACT
650  */
651 static bool
652 is_transact_command(const char *query)
653 {
654         int                     wordlen;
655
656         /*
657          * First we must advance over any whitespace and comments.
658          */
659         while (*query)
660         {
661                 if (isspace((unsigned char) *query))
662                         query++;
663                 else if (query[0] == '-' && query[1] == '-')
664                 {
665                         query += 2;
666                         while (*query && *query != '\n')
667                                 query++;
668                 }
669                 else if (query[0] == '/' && query[1] == '*')
670                 {
671                         query += 2;
672                         while (*query)
673                         {
674                                 if (query[0] == '*' && query[1] == '/')
675                                 {
676                                         query += 2;
677                                         break;
678                                 }
679                                 else
680                                         query++;
681                         }
682                 }
683                 else
684                         break;                          /* found first token */
685         }
686
687         /*
688          * Check word length ("beginx" is not "begin").
689          */
690         wordlen = 0;
691         while (isalpha((unsigned char) query[wordlen]))
692                 wordlen++;
693
694         if (wordlen == 5 && strncasecmp(query, "begin", 5) == 0)
695                 return true;
696         if (wordlen == 6 && strncasecmp(query, "commit", 6) == 0)
697                 return true;
698         if (wordlen == 8 && strncasecmp(query, "rollback", 8) == 0)
699                 return true;
700         if (wordlen == 5 && strncasecmp(query, "abort", 5) == 0)
701                 return true;
702         if (wordlen == 3 && strncasecmp(query, "end", 3) == 0)
703                 return true;
704         if (wordlen == 5 && strncasecmp(query, "start", 5) == 0)
705                 return true;
706
707         return false;
708 }
709
710
711 char
712 parse_char(char **buf)
713 {
714         long            l;
715
716         l = strtol(*buf, buf, 0);
717         --*buf;
718         return (char) l;
719 }
720
721
722 /*
723  * Test if the current user is a database superuser.
724  *
725  * Note: this will correctly detect superuserness only with a protocol-3.0
726  * or newer backend; otherwise it will always say "false".
727  */
728 bool
729 is_superuser(void)
730 {
731         const char *val;
732
733         if (!pset.db)
734                 return false;
735
736         val = PQparameterStatus(pset.db, "is_superuser");
737
738         if (val && strcmp(val, "on") == 0)
739                 return true;
740
741         return false;
742 }
743
744
745 /*
746  * Return the session user of the current connection.
747  *
748  * Note: this will correctly detect the session user only with a
749  * protocol-3.0 or newer backend; otherwise it will return the
750  * connection user.
751  */
752 const char *
753 session_username(void)
754 {
755         const char *val;
756
757         if (!pset.db)
758                 return NULL;
759
760         val = PQparameterStatus(pset.db, "session_authorization");
761         if (val)
762                 return val;
763         else
764                 return PQuser(pset.db);
765 }