]> granicus.if.org Git - postgresql/blob - src/bin/psql/common.c
Make the printing code somewhat more independent by not relying on
[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.57 2003/03/18 22:15:44 petere Exp $
7  */
8 #include "postgres_fe.h"
9 #include "common.h"
10
11 #include <errno.h>
12 #include <stdarg.h>
13 #ifndef HAVE_STRDUP
14 #include <strdup.h>
15 #endif
16 #include <signal.h>
17 #ifndef WIN32
18 #include <sys/time.h>
19 #include <unistd.h>                             /* for write() */
20 #include <setjmp.h>
21 #else
22 #include <io.h>                                 /* for _write() */
23 #include <win32.h>
24 #include <sys/timeb.h>                  /* for _ftime() */
25 #endif
26
27 #include "libpq-fe.h"
28 #include "pqsignal.h"
29
30 #include "settings.h"
31 #include "variables.h"
32 #include "copy.h"
33 #include "prompt.h"
34 #include "print.h"
35 #include "mainloop.h"
36
37 extern bool prompt_state;
38
39 /*
40  * "Safe" wrapper around strdup()
41  */
42 char *
43 xstrdup(const char *string)
44 {
45         char       *tmp;
46
47         if (!string)
48         {
49                 fprintf(stderr, gettext("%s: xstrdup: cannot duplicate null pointer (internal error)\n"),
50                                 pset.progname);
51                 exit(EXIT_FAILURE);
52         }
53         tmp = strdup(string);
54         if (!tmp)
55         {
56                 psql_error("out of memory\n");
57                 exit(EXIT_FAILURE);
58         }
59         return tmp;
60 }
61
62
63
64 /*
65  * setQFout
66  * -- handler for -o command line option and \o command
67  *
68  * Tries to open file fname (or pipe if fname starts with '|')
69  * and stores the file handle in pset)
70  * Upon failure, sets stdout and returns false.
71  */
72 bool
73 setQFout(const char *fname)
74 {
75         bool            status = true;
76
77         /* Close old file/pipe */
78         if (pset.queryFout && pset.queryFout != stdout && pset.queryFout != stderr)
79         {
80                 if (pset.queryFoutPipe)
81                         pclose(pset.queryFout);
82                 else
83                         fclose(pset.queryFout);
84         }
85
86         /* If no filename, set stdout */
87         if (!fname || fname[0] == '\0')
88         {
89                 pset.queryFout = stdout;
90                 pset.queryFoutPipe = false;
91         }
92         else if (*fname == '|')
93         {
94                 pset.queryFout = popen(fname + 1, "w");
95                 pset.queryFoutPipe = true;
96         }
97         else
98         {
99                 pset.queryFout = fopen(fname, "w");
100                 pset.queryFoutPipe = false;
101         }
102
103         if (!(pset.queryFout))
104         {
105                 psql_error("%s: %s\n", fname, strerror(errno));
106                 pset.queryFout = stdout;
107                 pset.queryFoutPipe = false;
108                 status = false;
109         }
110
111         /* Direct signals */
112 #ifndef WIN32
113         if (pset.queryFoutPipe)
114                 pqsignal(SIGPIPE, SIG_IGN);
115         else
116                 pqsignal(SIGPIPE, SIG_DFL);
117 #endif
118
119         return status;
120 }
121
122
123
124 /*
125  * Error reporting for scripts. Errors should look like
126  *       psql:filename:lineno: message
127  *
128  */
129 void
130 psql_error(const char *fmt,...)
131 {
132         va_list         ap;
133
134         fflush(stdout);
135         if (pset.queryFout != stdout)
136                 fflush(pset.queryFout);
137
138         if (pset.inputfile)
139                 fprintf(stderr, "%s:%s:%u: ", pset.progname, pset.inputfile, pset.lineno);
140         va_start(ap, fmt);
141         vfprintf(stderr, gettext(fmt), ap);
142         va_end(ap);
143 }
144
145
146
147 /*
148  * for backend Notice messages (INFO, WARNING, etc)
149  */
150 void
151 NoticeProcessor(void *arg, const char *message)
152 {
153         (void) arg;                                     /* not used */
154         psql_error("%s", message);
155 }
156
157
158
159 /*
160  * Code to support query cancellation
161  *
162  * Before we start a query, we enable a SIGINT signal catcher that sends a
163  * cancel request to the backend. Note that sending the cancel directly from
164  * the signal handler is safe because PQrequestCancel() is written to make it
165  * so. We use write() to print to stdout because it's better to use simple
166  * facilities in a signal handler.
167  */
168 PGconn     *cancelConn;
169 volatile bool cancel_pressed;
170
171 #ifndef WIN32
172
173 #define write_stderr(String) write(fileno(stderr), String, strlen(String))
174
175 void
176 handle_sigint(SIGNAL_ARGS)
177 {
178         int                     save_errno = errno;
179
180         /* Don't muck around if copying in or prompting for a password. */
181         if ((copy_in_state && pset.cur_cmd_interactive) || prompt_state)
182                 return;
183
184         if (cancelConn == NULL)
185                 siglongjmp(main_loop_jmp, 1);
186
187         cancel_pressed = true;
188
189         if (PQrequestCancel(cancelConn))
190                 write_stderr("Cancel request sent\n");
191         else
192         {
193                 write_stderr("Could not send cancel request: ");
194                 write_stderr(PQerrorMessage(cancelConn));
195         }
196         errno = save_errno;                     /* just in case the write changed it */
197 }
198 #endif   /* not WIN32 */
199
200
201 /*
202  * PSQLexec
203  *
204  * This is the way to send "backdoor" queries (those not directly entered
205  * by the user). It is subject to -E but not -e.
206  *
207  * If the given querystring generates multiple PGresults, normally the last
208  * one is returned to the caller.  However, if ignore_command_ok is TRUE,
209  * then PGresults with status PGRES_COMMAND_OK are ignored.  This is intended
210  * mainly to allow locutions such as "begin; select ...; commit".
211  */
212 PGresult *
213 PSQLexec(const char *query, bool ignore_command_ok)
214 {
215         PGresult   *res = NULL;
216         PGresult   *newres;
217         const char *var;
218         ExecStatusType rstatus;
219
220         if (!pset.db)
221         {
222                 psql_error("You are currently not connected to a database.\n");
223                 return NULL;
224         }
225
226         var = GetVariable(pset.vars, "ECHO_HIDDEN");
227         if (var)
228         {
229                 printf("********* QUERY **********\n"
230                            "%s\n"
231                            "**************************\n\n", query);
232                 fflush(stdout);
233         }
234
235         if (var && strcmp(var, "noexec") == 0)
236                 return NULL;
237
238         /* discard any uneaten results of past queries */
239         while ((newres = PQgetResult(pset.db)) != NULL)
240                 PQclear(newres);
241
242         cancelConn = pset.db;
243         if (PQsendQuery(pset.db, query))
244         {
245                 while ((newres = PQgetResult(pset.db)) != NULL)
246                 {
247                         rstatus = PQresultStatus(newres);
248                         if (ignore_command_ok && rstatus == PGRES_COMMAND_OK)
249                         {
250                                 PQclear(newres);
251                                 continue;
252                         }
253                         PQclear(res);
254                         res = newres;
255                         if (rstatus == PGRES_COPY_IN ||
256                                 rstatus == PGRES_COPY_OUT)
257                                 break;
258                 }
259         }
260         rstatus = PQresultStatus(res);
261         /* keep cancel connection for copy out state */
262         if (rstatus != PGRES_COPY_OUT)
263                 cancelConn = NULL;
264         if (rstatus == PGRES_COPY_IN)
265                 copy_in_state = true;
266
267         if (res && (rstatus == PGRES_COMMAND_OK ||
268                                 rstatus == PGRES_TUPLES_OK ||
269                                 rstatus == PGRES_COPY_IN ||
270                                 rstatus == PGRES_COPY_OUT))
271                 return res;
272         else
273         {
274                 psql_error("%s", PQerrorMessage(pset.db));
275                 PQclear(res);
276
277                 if (PQstatus(pset.db) == CONNECTION_BAD)
278                 {
279                         if (!pset.cur_cmd_interactive)
280                         {
281                                 psql_error("connection to server was lost\n");
282                                 exit(EXIT_BADCONN);
283                         }
284                         fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr);
285                         PQreset(pset.db);
286                         if (PQstatus(pset.db) == CONNECTION_BAD)
287                         {
288                                 fputs(gettext("Failed.\n"), stderr);
289                                 PQfinish(pset.db);
290                                 pset.db = NULL;
291                                 SetVariable(pset.vars, "DBNAME", NULL);
292                                 SetVariable(pset.vars, "HOST", NULL);
293                                 SetVariable(pset.vars, "PORT", NULL);
294                                 SetVariable(pset.vars, "USER", NULL);
295                                 SetVariable(pset.vars, "ENCODING", NULL);
296                         }
297                         else
298                                 fputs(gettext("Succeeded.\n"), stderr);
299                 }
300
301                 return NULL;
302         }
303 }
304
305
306
307 /*
308  * SendQuery: send the query string to the backend
309  * (and print out results)
310  *
311  * Note: This is the "front door" way to send a query. That is, use it to
312  * send queries actually entered by the user. These queries will be subject to
313  * single step mode.
314  * To send "back door" queries (generated by slash commands, etc.) in a
315  * controlled way, use PSQLexec().
316  *
317  * Returns true if the query executed successfully, false otherwise.
318  */
319 bool
320 SendQuery(const char *query)
321 {
322         bool            success = false;
323         PGresult   *results;
324         PGnotify   *notify;
325 #ifndef WIN32
326         struct timeval before,
327                                 after;
328 #else
329         struct _timeb before,
330                                 after;
331 #endif
332
333         if (!pset.db)
334         {
335                 psql_error("You are currently not connected to a database.\n");
336                 return false;
337         }
338
339         if (GetVariableBool(pset.vars, "SINGLESTEP"))
340         {
341                 char            buf[3];
342
343                 printf(gettext("***(Single step mode: Verify query)*********************************************\n"
344                                            "%s\n"
345                                            "***(press return to proceed or enter x and return to cancel)********************\n"),
346                            query);
347                 fflush(stdout);
348                 if (fgets(buf, sizeof(buf), stdin) != NULL)
349                         if (buf[0] == 'x')
350                                 return false;
351         }
352         else
353         {
354                 const char *var = GetVariable(pset.vars, "ECHO");
355
356                 if (var && strncmp(var, "queries", strlen(var)) == 0)
357                         puts(query);
358         }
359
360         cancelConn = pset.db;
361
362 #ifndef WIN32
363         if (pset.timing)
364                 gettimeofday(&before, NULL);
365         results = PQexec(pset.db, query);
366         if (pset.timing)
367                 gettimeofday(&after, NULL);
368 #else
369         if (pset.timing)
370                 _ftime(&before);
371         results = PQexec(pset.db, query);
372         if (pset.timing)
373                 _ftime(&after);
374 #endif
375
376         if (PQresultStatus(results) == PGRES_COPY_IN)
377                 copy_in_state = true;
378         /* keep cancel connection for copy out state */
379         if (PQresultStatus(results) != PGRES_COPY_OUT)
380                 cancelConn = NULL;
381
382         if (results == NULL)
383         {
384                 fputs(PQerrorMessage(pset.db), pset.queryFout);
385                 success = false;
386         }
387         else
388         {
389                 switch (PQresultStatus(results))
390                 {
391                         case PGRES_TUPLES_OK:
392                                 /* write output to \g argument, if any */
393                                 if (pset.gfname)
394                                 {
395                                         FILE       *queryFout_copy = pset.queryFout;
396                                         bool            queryFoutPipe_copy = pset.queryFoutPipe;
397
398                                         pset.queryFout = stdout;        /* so it doesn't get
399                                                                                                  * closed */
400
401                                         /* open file/pipe */
402                                         if (!setQFout(pset.gfname))
403                                         {
404                                                 pset.queryFout = queryFout_copy;
405                                                 pset.queryFoutPipe = queryFoutPipe_copy;
406                                                 success = false;
407                                                 break;
408                                         }
409
410                                         printQuery(results, &pset.popt, pset.queryFout);
411
412                                         /* close file/pipe, restore old setting */
413                                         setQFout(NULL);
414
415                                         pset.queryFout = queryFout_copy;
416                                         pset.queryFoutPipe = queryFoutPipe_copy;
417
418                                         free(pset.gfname);
419                                         pset.gfname = NULL;
420
421                                         success = true;
422                                 }
423                                 else
424                                 {
425                                         printQuery(results, &pset.popt, pset.queryFout);
426                                         success = true;
427                                 }
428                                 break;
429                         case PGRES_EMPTY_QUERY:
430                                 success = true;
431                                 break;
432                         case PGRES_COMMAND_OK:
433                                 {
434                                         char            buf[10];
435
436                                         success = true;
437                                         sprintf(buf, "%u", (unsigned int) PQoidValue(results));
438                                         if (!QUIET())
439                                                 fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
440                                         SetVariable(pset.vars, "LASTOID", buf);
441                                         break;
442                                 }
443                         case PGRES_COPY_OUT:
444                                 success = handleCopyOut(pset.db, pset.queryFout);
445                                 break;
446
447                         case PGRES_COPY_IN:
448                                 if (pset.cur_cmd_interactive && !QUIET())
449                                         puts(gettext("Enter data to be copied followed by a newline.\n"
450                                                                  "End with a backslash and a period on a line by itself."));
451
452                                 success = handleCopyIn(pset.db, pset.cur_cmd_source,
453                                                                            pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
454                                 break;
455
456                         case PGRES_NONFATAL_ERROR:
457                         case PGRES_FATAL_ERROR:
458                         case PGRES_BAD_RESPONSE:
459                                 success = false;
460                                 psql_error("%s", PQerrorMessage(pset.db));
461                                 break;
462                 }
463
464                 fflush(pset.queryFout);
465
466                 if (PQstatus(pset.db) == CONNECTION_BAD)
467                 {
468                         if (!pset.cur_cmd_interactive)
469                         {
470                                 psql_error("connection to server was lost\n");
471                                 exit(EXIT_BADCONN);
472                         }
473                         fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr);
474                         PQreset(pset.db);
475                         if (PQstatus(pset.db) == CONNECTION_BAD)
476                         {
477                                 fputs(gettext("Failed.\n"), stderr);
478                                 PQfinish(pset.db);
479                                 PQclear(results);
480                                 pset.db = NULL;
481                                 SetVariable(pset.vars, "DBNAME", NULL);
482                                 SetVariable(pset.vars, "HOST", NULL);
483                                 SetVariable(pset.vars, "PORT", NULL);
484                                 SetVariable(pset.vars, "USER", NULL);
485                                 SetVariable(pset.vars, "ENCODING", NULL);
486                                 return false;
487                         }
488                         else
489                                 fputs(gettext("Succeeded.\n"), stderr);
490                 }
491
492                 /* check for asynchronous notification returns */
493                 while ((notify = PQnotifies(pset.db)) != NULL)
494                 {
495                         fprintf(pset.queryFout, gettext("Asynchronous NOTIFY '%s' from backend with pid %d received.\n"),
496                                         notify->relname, notify->be_pid);
497                         free(notify);
498                         fflush(pset.queryFout);
499                 }
500
501                 if (results)
502                         PQclear(results);
503         }
504
505         /* Possible microtiming output */
506         if (pset.timing && success)
507 #ifndef WIN32
508                 printf(gettext("Time: %.2f ms\n"),
509                            ((after.tv_sec - before.tv_sec) * 1000000.0 + after.tv_usec - before.tv_usec) / 1000.0);
510 #else
511                 printf(gettext("Time: %.2f ms\n"),
512                            ((after.time - before.time) * 1000.0 + after.millitm - before.millitm));
513 #endif
514
515         return success;
516 }