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