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.51 2002/10/29 19:35:33 momjian Exp $
8 #include "postgres_fe.h"
19 #include <unistd.h> /* for write() */
22 #include <io.h> /* for _write() */
24 #include <sys/timeb.h> /* for _ftime() */
28 #include <sys/ioctl.h> /* for ioctl() */
39 #include "variables.h"
45 extern bool prompt_state;
48 * "Safe" wrapper around strdup()
51 xstrdup(const char *string)
57 fprintf(stderr, gettext("%s: xstrdup: cannot duplicate null pointer (internal error)\n"),
64 psql_error("out of memory\n");
74 * -- handler for -o command line option and \o command
76 * Tries to open file fname (or pipe if fname starts with '|')
77 * and stores the file handle in pset)
78 * Upon failure, sets stdout and returns false.
81 setQFout(const char *fname)
85 /* Close old file/pipe */
86 if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
88 if (pset.queryFoutPipe)
89 pclose(pset.queryFout);
91 fclose(pset.queryFout);
94 /* If no filename, set stdout */
95 if (!fname || fname[0] == '\0')
97 pset.queryFout = stdout;
98 pset.queryFoutPipe = false;
100 else if (*fname == '|')
102 pset.queryFout = popen(fname + 1, "w");
103 pset.queryFoutPipe = true;
107 pset.queryFout = fopen(fname, "w");
108 pset.queryFoutPipe = false;
111 if (!(pset.queryFout))
113 psql_error("%s: %s\n", fname, strerror(errno));
114 pset.queryFout = stdout;
115 pset.queryFoutPipe = false;
121 if (pset.queryFoutPipe)
122 pqsignal(SIGPIPE, SIG_IGN);
124 pqsignal(SIGPIPE, SIG_DFL);
133 * Error reporting for scripts. Errors should look like
134 * psql:filename:lineno: message
138 psql_error(const char *fmt,...)
143 if (pset.queryFout != stdout)
144 fflush(pset.queryFout);
147 fprintf(stderr, "%s:%s:%u: ", pset.progname, pset.inputfile, pset.lineno);
149 vfprintf(stderr, gettext(fmt), ap);
156 * for backend INFO, WARNING, ERROR
159 NoticeProcessor(void *arg, const char *message)
161 (void) arg; /* not used */
162 psql_error("%s", message);
168 * Code to support query cancellation
170 * Before we start a query, we enable a SIGINT signal catcher that sends a
171 * cancel request to the backend. Note that sending the cancel directly from
172 * the signal handler is safe because PQrequestCancel() is written to make it
173 * so. We use write() to print to stdout because it's better to use simple
174 * facilities in a signal handler.
177 volatile bool cancel_pressed;
181 #define write_stderr(String) write(fileno(stderr), String, strlen(String))
184 handle_sigint(SIGNAL_ARGS)
186 int save_errno = errno;
188 /* Don't muck around if copying in or prompting for a password. */
189 if ((copy_in_state && pset.cur_cmd_interactive) || prompt_state)
192 if (cancelConn == NULL)
193 siglongjmp(main_loop_jmp, 1);
195 cancel_pressed = true;
197 if (PQrequestCancel(cancelConn))
198 write_stderr("Cancel request sent\n");
201 write_stderr("Could not send cancel request: ");
202 write_stderr(PQerrorMessage(cancelConn));
204 errno = save_errno; /* just in case the write changed it */
206 #endif /* not WIN32 */
212 * This is the way to send "backdoor" queries (those not directly entered
213 * by the user). It is subject to -E but not -e.
215 * If the given querystring generates multiple PGresults, normally the last
216 * one is returned to the caller. However, if ignore_command_ok is TRUE,
217 * then PGresults with status PGRES_COMMAND_OK are ignored. This is intended
218 * mainly to allow locutions such as "begin; select ...; commit".
221 PSQLexec(const char *query, bool ignore_command_ok)
223 PGresult *res = NULL;
226 ExecStatusType rstatus;
230 psql_error("You are currently not connected to a database.\n");
234 var = GetVariable(pset.vars, "ECHO_HIDDEN");
237 printf("********* QUERY **********\n"
239 "**************************\n\n", query);
243 if (var && strcmp(var, "noexec") == 0)
246 /* discard any uneaten results of past queries */
247 while ((newres = PQgetResult(pset.db)) != NULL)
250 cancelConn = pset.db;
251 if (PQsendQuery(pset.db, query))
253 while ((newres = PQgetResult(pset.db)) != NULL)
255 rstatus = PQresultStatus(newres);
256 if (ignore_command_ok && rstatus == PGRES_COMMAND_OK)
263 if (rstatus == PGRES_COPY_IN ||
264 rstatus == PGRES_COPY_OUT)
268 rstatus = PQresultStatus(res);
269 /* keep cancel connection for copy out state */
270 if (rstatus != PGRES_COPY_OUT)
272 if (rstatus == PGRES_COPY_IN)
273 copy_in_state = true;
275 if (res && (rstatus == PGRES_COMMAND_OK ||
276 rstatus == PGRES_TUPLES_OK ||
277 rstatus == PGRES_COPY_IN ||
278 rstatus == PGRES_COPY_OUT))
282 psql_error("%s", PQerrorMessage(pset.db));
285 if (PQstatus(pset.db) == CONNECTION_BAD)
287 if (!pset.cur_cmd_interactive)
289 psql_error("connection to server was lost\n");
292 fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr);
294 if (PQstatus(pset.db) == CONNECTION_BAD)
296 fputs(gettext("Failed.\n"), stderr);
299 SetVariable(pset.vars, "DBNAME", NULL);
300 SetVariable(pset.vars, "HOST", NULL);
301 SetVariable(pset.vars, "PORT", NULL);
302 SetVariable(pset.vars, "USER", NULL);
303 SetVariable(pset.vars, "ENCODING", NULL);
306 fputs(gettext("Succeeded.\n"), stderr);
316 * SendQuery: send the query string to the backend
317 * (and print out results)
319 * Note: This is the "front door" way to send a query. That is, use it to
320 * send queries actually entered by the user. These queries will be subject to
322 * To send "back door" queries (generated by slash commands, etc.) in a
323 * controlled way, use PSQLexec().
325 * Returns true if the query executed successfully, false otherwise.
328 SendQuery(const char *query)
330 bool success = false;
334 struct timeval before,
337 struct _timeb before,
343 psql_error("You are currently not connected to a database.\n");
347 if (GetVariableBool(pset.vars, "SINGLESTEP"))
351 printf(gettext("***(Single step mode: Verify query)*********************************************\n"
353 "***(press return to proceed or enter x and return to cancel)********************\n"),
356 if (fgets(buf, sizeof(buf), stdin) != NULL)
362 const char *var = GetVariable(pset.vars, "ECHO");
364 if (var && strncmp(var, "queries", strlen(var)) == 0)
368 cancelConn = pset.db;
372 gettimeofday(&before, NULL);
373 results = PQexec(pset.db, query);
375 gettimeofday(&after, NULL);
379 results = PQexec(pset.db, query);
384 if (PQresultStatus(results) == PGRES_COPY_IN)
385 copy_in_state = true;
386 /* keep cancel connection for copy out state */
387 if (PQresultStatus(results) != PGRES_COPY_OUT)
392 fputs(PQerrorMessage(pset.db), pset.queryFout);
397 switch (PQresultStatus(results))
399 case PGRES_TUPLES_OK:
400 /* write output to \g argument, if any */
403 FILE *queryFout_copy = pset.queryFout;
404 bool queryFoutPipe_copy = pset.queryFoutPipe;
406 pset.queryFout = stdout; /* so it doesn't get
410 if (!setQFout(pset.gfname))
412 pset.queryFout = queryFout_copy;
413 pset.queryFoutPipe = queryFoutPipe_copy;
418 printQuery(results, &pset.popt, pset.queryFout);
420 /* close file/pipe, restore old setting */
423 pset.queryFout = queryFout_copy;
424 pset.queryFoutPipe = queryFoutPipe_copy;
433 printQuery(results, &pset.popt, pset.queryFout);
437 case PGRES_EMPTY_QUERY:
440 case PGRES_COMMAND_OK:
445 sprintf(buf, "%u", (unsigned int) PQoidValue(results));
447 fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
448 SetVariable(pset.vars, "LASTOID", buf);
452 success = handleCopyOut(pset.db, pset.queryFout);
456 if (pset.cur_cmd_interactive && !QUIET())
457 puts(gettext("Enter data to be copied followed by a newline.\n"
458 "End with a backslash and a period on a line by itself."));
460 success = handleCopyIn(pset.db, pset.cur_cmd_source,
461 pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
464 case PGRES_NONFATAL_ERROR:
465 case PGRES_FATAL_ERROR:
466 case PGRES_BAD_RESPONSE:
468 psql_error("%s", PQerrorMessage(pset.db));
472 fflush(pset.queryFout);
474 if (PQstatus(pset.db) == CONNECTION_BAD)
476 if (!pset.cur_cmd_interactive)
478 psql_error("connection to server was lost\n");
481 fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr);
483 if (PQstatus(pset.db) == CONNECTION_BAD)
485 fputs(gettext("Failed.\n"), stderr);
489 SetVariable(pset.vars, "DBNAME", NULL);
490 SetVariable(pset.vars, "HOST", NULL);
491 SetVariable(pset.vars, "PORT", NULL);
492 SetVariable(pset.vars, "USER", NULL);
493 SetVariable(pset.vars, "ENCODING", NULL);
497 fputs(gettext("Succeeded.\n"), stderr);
500 /* check for asynchronous notification returns */
501 while ((notify = PQnotifies(pset.db)) != NULL)
503 fprintf(pset.queryFout, gettext("Asynchronous NOTIFY '%s' from backend with pid %d received.\n"),
504 notify->relname, notify->be_pid);
506 fflush(pset.queryFout);
513 /* Possible microtiming output */
514 if (pset.timing && success)
516 printf(gettext("Time: %.2f ms\n"),
517 ((after.tv_sec - before.tv_sec) * 1000000.0 + after.tv_usec - before.tv_usec) / 1000.0);
519 printf(gettext("Time: %.2f ms\n"),
520 ((after.time - before.time) * 1000.0 + after.millitm - before.millitm));
530 * Tests if pager is needed and returns appropriate FILE pointer.
533 PageOutput(int lines, bool pager)
535 /* check whether we need / can / are supposed to use pager */
539 isatty(fileno(stdin)) &&
540 isatty(fileno(stdout))
544 const char *pagerprog;
548 struct winsize screen_size;
550 result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
551 if (result == -1 || lines > screen_size.ws_row)
554 pagerprog = getenv("PAGER");
556 pagerprog = DEFAULT_PAGER;
558 pqsignal(SIGPIPE, SIG_IGN);
560 return popen(pagerprog, "w");