]> granicus.if.org Git - postgresql/blob - src/bin/psql/common.c
Another pgindent run with updated typedefs.
[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.71 2003/08/08 21:42:24 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(void)
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                 psql_error("%s", PQerrorMessage(pset.db));
348                 CheckConnection();
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),
518                                                                                    pset.queryFout);
519                                                 fputs("</p>\n", pset.queryFout);
520                                         }
521                                         else
522                                                 fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
523                                 }
524                                 SetVariable(pset.vars, "LASTOID", buf);
525                                 break;
526                         }
527                 case PGRES_COPY_OUT:
528                         success = handleCopyOut(pset.db, pset.queryFout);
529                         break;
530
531                 case PGRES_COPY_IN:
532                         if (pset.cur_cmd_interactive && !QUIET())
533                                 puts(gettext("Enter data to be copied followed by a newline.\n"
534                                                          "End with a backslash and a period on a line by itself."));
535
536                         success = handleCopyIn(pset.db, pset.cur_cmd_source,
537                           pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
538                         break;
539
540                 default:
541                         break;
542         }
543
544         fflush(pset.queryFout);
545
546         /* may need this to recover from conn loss during COPY */
547         if (!CheckConnection())
548                 return false;
549
550         /* Possible microtiming output */
551         if (pset.timing && success)
552                 printf(gettext("Time: %.2f ms\n"), DIFF_MSEC(after, before));
553
554         return success;
555 }
556
557
558
559 /*
560  * SendQuery: send the query string to the backend
561  * (and print out results)
562  *
563  * Note: This is the "front door" way to send a query. That is, use it to
564  * send queries actually entered by the user. These queries will be subject to
565  * single step mode.
566  * To send "back door" queries (generated by slash commands, etc.) in a
567  * controlled way, use PSQLexec().
568  *
569  * Returns true if the query executed successfully, false otherwise.
570  */
571 bool
572 SendQuery(const char *query)
573 {
574         PGresult   *results;
575         TimevalStruct before,
576                                 after;
577         bool            OK;
578
579         if (!pset.db)
580         {
581                 psql_error("You are currently not connected to a database.\n");
582                 return false;
583         }
584
585         if (GetVariableBool(pset.vars, "SINGLESTEP"))
586         {
587                 char            buf[3];
588
589                 printf(gettext("***(Single step mode: verify command)*******************************************\n"
590                                            "%s\n"
591                                            "***(press return to proceed or enter x and return to cancel)********************\n"),
592                            query);
593                 fflush(stdout);
594                 if (fgets(buf, sizeof(buf), stdin) != NULL)
595                         if (buf[0] == 'x')
596                                 return false;
597         }
598         else if (VariableEquals(pset.vars, "ECHO", "queries"))
599         {
600                 puts(query);
601                 fflush(stdout);
602         }
603
604         SetCancelConn();
605
606         if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
607                 !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
608                 !is_transact_command(query))
609         {
610                 results = PQexec(pset.db, "BEGIN");
611                 if (PQresultStatus(results) != PGRES_COMMAND_OK)
612                 {
613                         psql_error("%s", PQerrorMessage(pset.db));
614                         PQclear(results);
615                         ResetCancelConn();
616                         return false;
617                 }
618                 PQclear(results);
619         }
620
621         if (pset.timing)
622                 GETTIMEOFDAY(&before);
623         results = PQexec(pset.db, query);
624         if (pset.timing)
625                 GETTIMEOFDAY(&after);
626
627         OK = (AcceptResult(results) && PrintQueryResults(results, &before, &after));
628         PQclear(results);
629
630         PrintNotifications();
631         return OK;
632 }
633
634 /*
635  * check whether a query string begins with BEGIN/COMMIT/ROLLBACK/START XACT
636  */
637 static bool
638 is_transact_command(const char *query)
639 {
640         int                     wordlen;
641
642         /*
643          * First we must advance over any whitespace and comments.
644          */
645         while (*query)
646         {
647                 if (isspace((unsigned char) *query))
648                         query++;
649                 else if (query[0] == '-' && query[1] == '-')
650                 {
651                         query += 2;
652                         while (*query && *query != '\n')
653                                 query++;
654                 }
655                 else if (query[0] == '/' && query[1] == '*')
656                 {
657                         query += 2;
658                         while (*query)
659                         {
660                                 if (query[0] == '*' && query[1] == '/')
661                                 {
662                                         query += 2;
663                                         break;
664                                 }
665                                 else
666                                         query++;
667                         }
668                 }
669                 else
670                         break;                          /* found first token */
671         }
672
673         /*
674          * Check word length ("beginx" is not "begin").
675          */
676         wordlen = 0;
677         while (isalpha((unsigned char) query[wordlen]))
678                 wordlen++;
679
680         if (wordlen == 5 && strncasecmp(query, "begin", 5) == 0)
681                 return true;
682         if (wordlen == 6 && strncasecmp(query, "commit", 6) == 0)
683                 return true;
684         if (wordlen == 8 && strncasecmp(query, "rollback", 8) == 0)
685                 return true;
686         if (wordlen == 5 && strncasecmp(query, "abort", 5) == 0)
687                 return true;
688         if (wordlen == 3 && strncasecmp(query, "end", 3) == 0)
689                 return true;
690         if (wordlen == 5 && strncasecmp(query, "start", 5) == 0)
691                 return true;
692
693         return false;
694 }
695
696
697 char
698 parse_char(char **buf)
699 {
700         long            l;
701
702         l = strtol(*buf, buf, 0);
703         --*buf;
704         return (char) l;
705 }
706
707
708 /*
709  * Test if the current user is a database superuser.
710  *
711  * Note: this will correctly detect superuserness only with a protocol-3.0
712  * or newer backend; otherwise it will always say "false".
713  */
714 bool
715 is_superuser(void)
716 {
717         const char *val;
718
719         if (!pset.db)
720                 return false;
721
722         val = PQparameterStatus(pset.db, "is_superuser");
723
724         if (val && strcmp(val, "on") == 0)
725                 return true;
726
727         return false;
728 }