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