]> granicus.if.org Git - postgresql/blob - src/bin/psql/common.c
Fix missing \n in some psql_error calls.
[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.15 2000/02/20 02:37:40 tgl Exp $
7  */
8 #include "postgres.h"
9 #include "common.h"
10
11 #include <errno.h>
12 #include <stdarg.h>
13 #ifdef HAVE_TERMIOS_H
14 #include <termios.h>
15 #endif
16 #ifndef HAVE_STRDUP
17 #include <strdup.h>
18 #endif
19 #include <signal.h>
20 #ifndef WIN32
21 #include <unistd.h>                             /* for write() */
22 #else
23 #include <io.h>                 /* for _write() */
24 #include <win32.h>
25 #endif
26
27 #include "libpq-fe.h"
28 #include "postgres_ext.h"
29 #include "pqsignal.h"
30 #include "version.h"
31
32 #include "settings.h"
33 #include "variables.h"
34 #include "copy.h"
35 #include "prompt.h"
36 #include "print.h"
37
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, "%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, 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 stdin.
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 char *
173 simple_prompt(const char *prompt, int maxlen, bool echo)
174 {
175         int                     length;
176         char       *destination;
177
178 #ifdef HAVE_TERMIOS_H
179         struct termios t_orig,
180                                 t;
181 #endif
182
183         destination = (char *) malloc(maxlen + 2);
184         if (!destination)
185                 return NULL;
186         if (prompt)
187                 fputs(prompt, stdout);
188
189 #ifdef HAVE_TERMIOS_H
190         if (!echo)
191         {
192                 tcgetattr(0, &t);
193                 t_orig = t;
194                 t.c_lflag &= ~ECHO;
195                 tcsetattr(0, TCSADRAIN, &t);
196         }
197 #endif
198
199         fgets(destination, maxlen, stdin);
200
201 #ifdef HAVE_TERMIOS_H
202         if (!echo)
203         {
204                 tcsetattr(0, TCSADRAIN, &t_orig);
205                 puts("");
206         }
207 #endif
208
209         length = strlen(destination);
210         if (length > 0 && destination[length - 1] != '\n')
211         {
212                 /* eat rest of the line */
213                 char            buf[512];
214
215                 do
216                 {
217                         fgets(buf, 512, stdin);
218                 } while (buf[strlen(buf) - 1] != '\n');
219         }
220
221         if (length > 0 && destination[length - 1] == '\n')
222                 /* remove trailing newline */
223                 destination[length - 1] = '\0';
224
225         return destination;
226 }
227
228
229
230 /*
231  * Code to support query cancellation
232  *
233  * Before we start a query, we enable a SIGINT signal catcher that sends a
234  * cancel request to the backend. Note that sending the cancel directly from
235  * the signal handler is safe because PQrequestCancel() is written to make it
236  * so. We use write() to print to stdout because it's better to use simple
237  * facilities in a signal handler.
238  */
239
240 static PGconn *cancelConn;
241
242 #define write_stderr(String) write(fileno(stderr), String, strlen(String))
243
244 static void
245 handle_sigint(SIGNAL_ARGS)
246 {
247         if (cancelConn == NULL)
248                 return;
249         /* Try to send cancel request */
250         if (PQrequestCancel(cancelConn))
251                 write_stderr("\nCancel request sent\n");
252         else
253         {
254                 write_stderr("\nCould not send cancel request: ");
255                 write_stderr(PQerrorMessage(cancelConn));
256         }
257 }
258
259
260
261 /*
262  * PSQLexec
263  *
264  * This is the way to send "backdoor" queries (those not directly entered
265  * by the user). It is subject to -E but not -e.
266  */
267 PGresult   *
268 PSQLexec(const char *query)
269 {
270         PGresult   *res;
271         const char *var;
272
273         if (!pset.db)
274         {
275         psql_error("You are currently not connected to a database.\n");
276                 return NULL;
277         }
278
279         var = GetVariable(pset.vars, "ECHO_HIDDEN");
280         if (var)
281         {
282                 printf("********* QUERY *********\n%s\n*************************\n\n", query);
283                 fflush(stdout);
284         }
285
286         if (var && strcmp(var, "noexec") == 0)
287                 return NULL;
288
289         cancelConn = pset.db;
290 #ifndef WIN32
291         pqsignal(SIGINT, handle_sigint);        /* control-C => cancel */
292 #endif
293
294         res = PQexec(pset.db, query);
295
296 #ifndef WIN32
297         pqsignal(SIGINT, SIG_DFL);      /* now control-C is back to normal */
298 #endif
299
300         if (PQstatus(pset.db) == CONNECTION_BAD)
301         {
302         if (!pset.cur_cmd_interactive)
303         {
304             psql_error("connection to server was lost\n");
305             exit(EXIT_BADCONN);
306         }
307                 fputs("The connection to the server was lost. Attempting reset: ", stderr);
308                 PQreset(pset.db);
309                 if (PQstatus(pset.db) == CONNECTION_BAD)
310                 {
311                         fputs("Failed.\n", stderr);
312                         PQfinish(pset.db);
313                         PQclear(res);
314                         pset.db = NULL;
315             SetVariable(pset.vars, "DBNAME", NULL);
316             SetVariable(pset.vars, "HOST", NULL);
317             SetVariable(pset.vars, "PORT", NULL);
318             SetVariable(pset.vars, "USER", NULL);
319                         return NULL;
320                 }
321                 else
322                         fputs("Succeeded.\n", stderr);
323         }
324
325         if (res && (PQresultStatus(res) == PGRES_COMMAND_OK ||
326                                 PQresultStatus(res) == PGRES_TUPLES_OK ||
327                                 PQresultStatus(res) == PGRES_COPY_IN ||
328                                 PQresultStatus(res) == PGRES_COPY_OUT)
329                 )
330                 return res;
331         else
332         {
333         psql_error("%s", PQerrorMessage(pset.db));
334                 PQclear(res);
335                 return NULL;
336         }
337 }
338
339
340
341 /*
342  * SendQuery: send the query string to the backend
343  * (and print out results)
344  *
345  * Note: This is the "front door" way to send a query. That is, use it to
346  * send queries actually entered by the user. These queries will be subject to
347  * single step mode.
348  * To send "back door" queries (generated by slash commands, etc.) in a
349  * controlled way, use PSQLexec().
350  *
351  * Returns true if the query executed successfully, false otherwise.
352  */
353 bool
354 SendQuery(const char *query)
355 {
356         bool            success = false;
357         PGresult   *results;
358         PGnotify   *notify;
359
360         if (!pset.db)
361         {
362         psql_error("you are currently not connected to a database.\n");
363                 return false;
364         }
365
366         if (GetVariableBool(pset.vars, "SINGLESTEP"))
367         {
368                 char            buf[3];
369
370                 printf("***(Single step mode: Verify query)*********************************************\n"
371                "%s\n"
372                "***(press return to proceed or enter x and return to cancel)********************\n",
373                query);
374                 fflush(stdout);
375                 fgets(buf, 3, stdin);
376                 if (buf[0] == 'x')
377                         return false;
378         }
379     else
380     {
381         const char * var = GetVariable(pset.vars, "ECHO");
382         if (var && strncmp(var, "queries", strlen(var))==0)
383             puts(query);
384     }
385
386         cancelConn = pset.db;
387 #ifndef WIN32
388         pqsignal(SIGINT, handle_sigint);
389 #endif
390
391         results = PQexec(pset.db, query);
392
393 #ifndef WIN32
394         pqsignal(SIGINT, SIG_DFL);
395 #endif
396
397         if (results == NULL)
398         {
399                 fputs(PQerrorMessage(pset.db), pset.queryFout);
400                 success = false;
401         }
402         else
403         {
404                 switch (PQresultStatus(results))
405                 {
406                         case PGRES_TUPLES_OK:
407                 /* write output to \g argument, if any */
408                                 if (pset.gfname)
409                                 {
410                                         FILE * queryFout_copy = pset.queryFout;
411                     bool queryFoutPipe_copy = pset.queryFoutPipe;
412                     pset.queryFout = NULL; /* so it doesn't get closed */
413
414                     /* open file/pipe */
415                                         if (!setQFout(pset.gfname))
416                                         {
417                                                 success = false;
418                                                 break;
419                                         }
420
421                                         printQuery(results, &pset.popt, pset.queryFout);
422
423                                         /* close file/pipe */
424                                         setQFout(NULL);
425
426                                         free(pset.gfname);
427                                         pset.gfname = NULL;
428
429                                         pset.queryFout = queryFout_copy;
430                     pset.queryFoutPipe = queryFoutPipe_copy;
431
432                                         success = true;
433                                         break;
434                                 }
435                                 else
436                                 {
437                                         success = true;
438                                         printQuery(results, &pset.popt, pset.queryFout);
439                                 }
440                                 break;
441                         case PGRES_EMPTY_QUERY:
442                                 success = true;
443                                 break;
444                         case PGRES_COMMAND_OK:
445             {
446                 char buf[10];
447
448                                 success = true;
449                 sprintf(buf, "%u", (unsigned int)PQoidValue(results));
450                 if (!QUIET())
451                     fprintf(pset.queryFout, "%s\n", PQcmdStatus(results));
452                 SetVariable(pset.vars, "LASTOID", buf);
453                                 break;
454             }
455                         case PGRES_COPY_OUT:
456                                 success = handleCopyOut(pset.db, pset.queryFout);
457                                 break;
458
459                         case PGRES_COPY_IN:
460                                 if (pset.cur_cmd_interactive && !QUIET())
461                                         puts("Enter data to be copied followed by a newline.\n"
462                                                  "End with a backslash and a period on a line by itself.");
463
464                                 success = handleCopyIn(pset.db, pset.cur_cmd_source,
465                                                                            pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL);
466                                 break;
467
468                         case PGRES_NONFATAL_ERROR:
469                         case PGRES_FATAL_ERROR:
470                         case PGRES_BAD_RESPONSE:
471                                 success = false;
472                 psql_error("%s", PQerrorMessage(pset.db));
473                                 break;
474                 }
475
476         fflush(pset.queryFout);
477
478                 if (PQstatus(pset.db) == CONNECTION_BAD)
479                 {
480             if (!pset.cur_cmd_interactive)
481             {
482                 psql_error("connection to server was lost\n");
483                 exit(EXIT_BADCONN);
484             }
485                         fputs("The connection to the server was lost. Attempting reset: ", stderr);
486                         PQreset(pset.db);
487                         if (PQstatus(pset.db) == CONNECTION_BAD)
488                         {
489                                 fputs("Failed.\n", stderr);
490                                 PQfinish(pset.db);
491                                 PQclear(results);
492                                 pset.db = NULL;
493                 SetVariable(pset.vars, "DBNAME", NULL);
494                 SetVariable(pset.vars, "HOST", NULL);
495                 SetVariable(pset.vars, "PORT", NULL);
496                 SetVariable(pset.vars, "USER", NULL);
497                                 return false;
498                         }
499                         else
500                                 fputs("Succeeded.\n", stderr);
501                 }
502         
503                 /* check for asynchronous notification returns */
504                 while ((notify = PQnotifies(pset.db)) != NULL)
505                 {
506                         fprintf(pset.queryFout, "Asynchronous NOTIFY '%s' from backend with pid '%d' received.\n",
507                                         notify->relname, notify->be_pid);
508                         free(notify);
509             fflush(pset.queryFout);
510                 }
511
512                 if (results)
513                         PQclear(results);
514     }
515
516         return success;
517 }