2 * psql - the PostgreSQL interactive terminal
4 * Copyright 2000 by PostgreSQL Global Development Group
6 * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.67 2003/07/31 04:23:40 momjian Exp $
8 #include "postgres_fe.h"
20 #include <unistd.h> /* for write() */
23 #include <io.h> /* for _write() */
25 #include <sys/timeb.h> /* for _ftime() */
32 #include "variables.h"
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.
46 typedef struct timeval TimevalStruct;
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)
52 typedef struct _timeb TimevalStruct;
53 #define GETTIMEOFDAY(T) _ftime(T)
54 #define DIFF_MSEC(T, U) ((((T)->time - (U)->time) * 1000.0 + (T)->millitm - (U)->millitm))
58 extern bool prompt_state;
61 static bool is_transact_command(const char *query);
65 * "Safe" wrapper around strdup()
68 xstrdup(const char *string)
74 fprintf(stderr, gettext("%s: xstrdup: cannot duplicate null pointer (internal error)\n"),
81 psql_error("out of memory\n");
91 * -- handler for -o command line option and \o command
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.
98 setQFout(const char *fname)
102 /* Close old file/pipe */
103 if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
105 if (pset.queryFoutPipe)
106 pclose(pset.queryFout);
108 fclose(pset.queryFout);
111 /* If no filename, set stdout */
112 if (!fname || fname[0] == '\0')
114 pset.queryFout = stdout;
115 pset.queryFoutPipe = false;
117 else if (*fname == '|')
119 pset.queryFout = popen(fname + 1, "w");
120 pset.queryFoutPipe = true;
124 pset.queryFout = fopen(fname, "w");
125 pset.queryFoutPipe = false;
128 if (!(pset.queryFout))
130 psql_error("%s: %s\n", fname, strerror(errno));
131 pset.queryFout = stdout;
132 pset.queryFoutPipe = false;
138 pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL);
147 * Error reporting for scripts. Errors should look like
148 * psql:filename:lineno: message
152 psql_error(const char *fmt,...)
157 if (pset.queryFout != stdout)
158 fflush(pset.queryFout);
161 fprintf(stderr, "%s:%s:%u: ", pset.progname, pset.inputfile, pset.lineno);
163 vfprintf(stderr, gettext(fmt), ap);
170 * for backend Notice messages (INFO, WARNING, etc)
173 NoticeProcessor(void *arg, const char *message)
175 (void) arg; /* not used */
176 psql_error("%s", message);
182 * Code to support query cancellation
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.
190 static PGconn *volatile cancelConn = NULL;
192 volatile bool cancel_pressed = false;
197 #define write_stderr(String) write(fileno(stderr), String, strlen(String))
200 handle_sigint(SIGNAL_ARGS)
202 int save_errno = errno;
204 /* Don't muck around if prompting for a password. */
208 if (cancelConn == NULL)
209 siglongjmp(main_loop_jmp, 1);
211 cancel_pressed = true;
213 if (PQrequestCancel(cancelConn))
214 write_stderr("Cancel request sent\n");
217 write_stderr("Could not send cancel request: ");
218 write_stderr(PQerrorMessage(cancelConn));
220 errno = save_errno; /* just in case the write changed it */
222 #endif /* not WIN32 */
228 * Returns whether our backend connection is still there.
233 return PQstatus(pset.db) != CONNECTION_BAD;
240 * Verify that we still have a good connection to the backend, and if not,
241 * see if it can be restored.
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.
256 if (!pset.cur_cmd_interactive)
258 psql_error("connection to server was lost\n");
262 fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr);
267 fputs(gettext("Failed.\n"), stderr);
274 fputs(gettext("Succeeded.\n"), stderr);
285 * Set cancelConn to point to the current database connection.
287 static void SetCancelConn(void)
289 cancelConn = pset.db;
296 * Set cancelConn to NULL. I don't know what this means exactly, but it saves
297 * having to export the variable.
299 void ResetCancelConn(void)
308 * Checks whether a result is valid, giving an error message if necessary;
309 * resets cancelConn as needed, and ensures that the connection to the backend
312 * Returns true for valid result, false for error state.
315 AcceptResult(const PGresult *result)
325 else switch (PQresultStatus(result))
327 case PGRES_COMMAND_OK:
328 case PGRES_TUPLES_OK:
330 /* Fine, do nothing */
334 /* keep cancel connection for copy out state */
346 psql_error("%s", PQerrorMessage(pset.db));
357 * This is the way to send "backdoor" queries (those not directly entered
358 * by the user). It is subject to -E but not -e.
360 * In autocommit-off mode, a new transaction block is started if start_xact
361 * is true; nothing special is done when start_xact is false. Typically,
362 * start_xact = false is used for SELECTs and explicit BEGIN/COMMIT commands.
365 PSQLexec(const char *query, bool start_xact)
372 psql_error("You are currently not connected to a database.\n");
376 echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL);
377 if (echo_hidden != VAR_NOTSET)
379 printf("********* QUERY **********\n"
381 "**************************\n\n", query);
384 if (echo_hidden == 1) /* noexec? */
390 if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
391 !GetVariableBool(pset.vars, "AUTOCOMMIT"))
393 res = PQexec(pset.db, "BEGIN");
394 if (PQresultStatus(res) != PGRES_COMMAND_OK)
396 psql_error("%s", PQerrorMessage(pset.db));
404 res = PQexec(pset.db, query);
406 if (!AcceptResult(res) && res)
418 * PrintNotifications: check for asynchronous notifications, and print them out
422 PrintNotifications(void)
426 while ((notify = PQnotifies(pset.db)))
428 fprintf(pset.queryFout, gettext("Asynchronous notification \"%s\" received from server process with PID %d.\n"),
429 notify->relname, notify->be_pid);
431 fflush(pset.queryFout);
437 * PrintQueryTuples: assuming query result is OK, print its tuples
439 * Returns true if successful, false otherwise.
442 PrintQueryTuples(const PGresult *results)
444 /* write output to \g argument, if any */
447 FILE *queryFout_copy = pset.queryFout;
448 bool queryFoutPipe_copy = pset.queryFoutPipe;
450 pset.queryFout = stdout; /* so it doesn't get
454 if (!setQFout(pset.gfname))
456 pset.queryFout = queryFout_copy;
457 pset.queryFoutPipe = queryFoutPipe_copy;
461 printQuery(results, &pset.popt, pset.queryFout);
463 /* close file/pipe, restore old setting */
466 pset.queryFout = queryFout_copy;
467 pset.queryFoutPipe = queryFoutPipe_copy;
474 printQuery(results, &pset.popt, pset.queryFout);
483 * PrintQueryResults: analyze query results and print them out
485 * Note: Utility function for use by SendQuery() only.
487 * Returns true if the query executed successfully, false otherwise.
490 PrintQueryResults(PGresult *results,
491 const TimevalStruct *before,
492 const TimevalStruct *after)
494 bool success = false;
499 switch (PQresultStatus(results))
501 case PGRES_TUPLES_OK:
502 success = PrintQueryTuples(results);
504 case PGRES_EMPTY_QUERY:
507 case PGRES_COMMAND_OK:
512 sprintf(buf, "%u", (unsigned int) PQoidValue(results));
515 if (pset.popt.topt.format == PRINT_HTML)
517 fputs("<p>", pset.queryFout);
518 html_escaped_print(PQcmdStatus(results), pset.queryFout);
519 fputs("</p>\n", pset.queryFout);
523 fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
526 SetVariable(pset.vars, "LASTOID", buf);
530 success = handleCopyOut(pset.db, pset.queryFout);
534 if (pset.cur_cmd_interactive && !QUIET())
535 puts(gettext("Enter data to be copied followed by a newline.\n"
536 "End with a backslash and a period on a line by itself."));
538 success = handleCopyIn(pset.db, pset.cur_cmd_source,
539 pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
546 fflush(pset.queryFout);
548 if (!CheckConnection()) return false;
550 /* Possible microtiming output */
551 if (pset.timing && success)
552 printf(gettext("Time: %.2f ms\n"), DIFF_MSEC(after, before));
560 * SendQuery: send the query string to the backend
561 * (and print out results)
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
566 * To send "back door" queries (generated by slash commands, etc.) in a
567 * controlled way, use PSQLexec().
569 * Returns true if the query executed successfully, false otherwise.
572 SendQuery(const char *query)
575 TimevalStruct before, after;
580 psql_error("You are currently not connected to a database.\n");
584 if (GetVariableBool(pset.vars, "SINGLESTEP"))
588 printf(gettext("***(Single step mode: verify command)*******************************************\n"
590 "***(press return to proceed or enter x and return to cancel)********************\n"),
593 if (fgets(buf, sizeof(buf), stdin) != NULL)
597 else if (VariableEquals(pset.vars, "ECHO", "queries"))
605 if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
606 !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
607 !is_transact_command(query))
609 results = PQexec(pset.db, "BEGIN");
610 if (PQresultStatus(results) != PGRES_COMMAND_OK)
612 psql_error("%s", PQerrorMessage(pset.db));
621 GETTIMEOFDAY(&before);
622 results = PQexec(pset.db, query);
624 GETTIMEOFDAY(&after);
626 OK = (AcceptResult(results) && PrintQueryResults(results, &before, &after));
629 PrintNotifications();
634 * check whether a query string begins with BEGIN/COMMIT/ROLLBACK/START XACT
637 is_transact_command(const char *query)
642 * First we must advance over any whitespace and comments.
646 if (isspace((unsigned char) *query))
648 else if (query[0] == '-' && query[1] == '-')
651 while (*query && *query != '\n')
654 else if (query[0] == '/' && query[1] == '*')
659 if (query[0] == '*' && query[1] == '/')
669 break; /* found first token */
673 * Check word length ("beginx" is not "begin").
676 while (isalpha((unsigned char) query[wordlen]))
679 if (wordlen == 5 && strncasecmp(query, "begin", 5) == 0)
681 if (wordlen == 6 && strncasecmp(query, "commit", 6) == 0)
683 if (wordlen == 8 && strncasecmp(query, "rollback", 8) == 0)
685 if (wordlen == 5 && strncasecmp(query, "abort", 5) == 0)
687 if (wordlen == 3 && strncasecmp(query, "end", 3) == 0)
689 if (wordlen == 5 && strncasecmp(query, "start", 5) == 0)
696 char parse_char(char **buf)
700 l = strtol(*buf, buf, 0);
707 * Test if the current user is a database superuser.
709 * Note: this will correctly detect superuserness only with a protocol-3.0
710 * or newer backend; otherwise it will always say "false".
720 val = PQparameterStatus(pset.db, "is_superuser");
722 if (val && strcmp(val, "on") == 0)