2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2003, PostgreSQL Global Development Group
6 * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.75 2003/10/05 22:36:00 momjian Exp $
8 #include "postgres_fe.h"
18 #include <unistd.h> /* for write() */
21 #include <io.h> /* for _write() */
23 #include <sys/timeb.h> /* for _ftime() */
30 #include "variables.h"
36 #include "mb/pg_wchar.h"
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.
45 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;
54 #define GETTIMEOFDAY(T) _ftime(T)
55 #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.
249 CheckConnection(void)
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.
290 cancelConn = pset.db;
297 * Set cancelConn to NULL. I don't know what this means exactly, but it saves
298 * having to export the variable.
301 ResetCancelConn(void)
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
314 * Returns true for valid result, false for error state.
317 AcceptResult(const PGresult *result)
326 switch (PQresultStatus(result))
328 case PGRES_COMMAND_OK:
329 case PGRES_TUPLES_OK:
331 /* Fine, do nothing */
335 /* keep cancel connection for copy out state */
346 psql_error("%s", PQerrorMessage(pset.db));
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.
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.
365 * Note: we don't bother to check PQclientEncoding; it is assumed that no
366 * caller uses this path to issue "SET CLIENT_ENCODING".
369 PSQLexec(const char *query, bool start_xact)
376 psql_error("You are currently not connected to a database.\n");
380 echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL);
381 if (echo_hidden != VAR_NOTSET)
383 printf("********* QUERY **********\n"
385 "**************************\n\n", query);
388 if (echo_hidden == 1) /* noexec? */
394 if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
395 !GetVariableBool(pset.vars, "AUTOCOMMIT"))
397 res = PQexec(pset.db, "BEGIN");
398 if (PQresultStatus(res) != PGRES_COMMAND_OK)
400 psql_error("%s", PQerrorMessage(pset.db));
408 res = PQexec(pset.db, query);
410 if (!AcceptResult(res) && res)
422 * PrintNotifications: check for asynchronous notifications, and print them out
425 PrintNotifications(void)
429 while ((notify = PQnotifies(pset.db)))
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);
440 * PrintQueryTuples: assuming query result is OK, print its tuples
442 * Returns true if successful, false otherwise.
445 PrintQueryTuples(const PGresult *results)
447 /* write output to \g argument, if any */
450 FILE *queryFout_copy = pset.queryFout;
451 bool queryFoutPipe_copy = pset.queryFoutPipe;
453 pset.queryFout = stdout; /* so it doesn't get closed */
456 if (!setQFout(pset.gfname))
458 pset.queryFout = queryFout_copy;
459 pset.queryFoutPipe = queryFoutPipe_copy;
463 printQuery(results, &pset.popt, pset.queryFout);
465 /* close file/pipe, restore old setting */
468 pset.queryFout = queryFout_copy;
469 pset.queryFoutPipe = queryFoutPipe_copy;
475 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),
520 fputs("</p>\n", pset.queryFout);
523 fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
525 SetVariable(pset.vars, "LASTOID", buf);
529 success = handleCopyOut(pset.db, pset.queryFout);
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."));
537 success = handleCopyIn(pset.db, pset.cur_cmd_source,
538 pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
545 fflush(pset.queryFout);
547 /* may need this to recover from conn loss during COPY */
548 if (!CheckConnection())
551 /* Possible microtiming output */
552 if (pset.timing && success)
553 printf(gettext("Time: %.3f ms\n"), DIFF_MSEC(after, before));
561 * SendQuery: send the query string to the backend
562 * (and print out results)
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
567 * To send "back door" queries (generated by slash commands, etc.) in a
568 * controlled way, use PSQLexec().
570 * Returns true if the query executed successfully, false otherwise.
573 SendQuery(const char *query)
576 TimevalStruct before,
582 psql_error("You are currently not connected to a database.\n");
586 if (GetVariableBool(pset.vars, "SINGLESTEP"))
590 printf(gettext("***(Single step mode: verify command)*******************************************\n"
592 "***(press return to proceed or enter x and return to cancel)********************\n"),
595 if (fgets(buf, sizeof(buf), stdin) != NULL)
599 else if (VariableEquals(pset.vars, "ECHO", "queries"))
607 if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
608 !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
609 !is_transact_command(query))
611 results = PQexec(pset.db, "BEGIN");
612 if (PQresultStatus(results) != PGRES_COMMAND_OK)
614 psql_error("%s", PQerrorMessage(pset.db));
623 GETTIMEOFDAY(&before);
624 results = PQexec(pset.db, query);
626 GETTIMEOFDAY(&after);
628 OK = (AcceptResult(results) && PrintQueryResults(results, &before, &after));
631 /* check for events that may occur during query execution */
633 if (pset.encoding != PQclientEncoding(pset.db) &&
634 PQclientEncoding(pset.db) >= 0)
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));
643 PrintNotifications();
649 * check whether a query string begins with BEGIN/COMMIT/ROLLBACK/START XACT
652 is_transact_command(const char *query)
657 * First we must advance over any whitespace and comments.
661 if (isspace((unsigned char) *query))
663 else if (query[0] == '-' && query[1] == '-')
666 while (*query && *query != '\n')
669 else if (query[0] == '/' && query[1] == '*')
674 if (query[0] == '*' && query[1] == '/')
684 break; /* found first token */
688 * Check word length ("beginx" is not "begin").
691 while (isalpha((unsigned char) query[wordlen]))
694 if (wordlen == 5 && strncasecmp(query, "begin", 5) == 0)
696 if (wordlen == 6 && strncasecmp(query, "commit", 6) == 0)
698 if (wordlen == 8 && strncasecmp(query, "rollback", 8) == 0)
700 if (wordlen == 5 && strncasecmp(query, "abort", 5) == 0)
702 if (wordlen == 3 && strncasecmp(query, "end", 3) == 0)
704 if (wordlen == 5 && strncasecmp(query, "start", 5) == 0)
712 parse_char(char **buf)
716 l = strtol(*buf, buf, 0);
723 * Test if the current user is a database superuser.
725 * Note: this will correctly detect superuserness only with a protocol-3.0
726 * or newer backend; otherwise it will always say "false".
736 val = PQparameterStatus(pset.db, "is_superuser");
738 if (val && strcmp(val, "on") == 0)
746 * Return the session user of the current connection.
748 * Note: this will correctly detect the session user only with a
749 * protocol-3.0 or newer backend; otherwise it will return the
753 session_username(void)
760 val = PQparameterStatus(pset.db, "session_authorization");
764 return PQuser(pset.db);