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