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