]> granicus.if.org Git - postgresql/blob - src/bin/psql/psql.c
applied fixes for psql
[postgresql] / src / bin / psql / psql.c
1 /*-------------------------------------------------------------------------
2  *
3  * psql.c--
4  *    an interactive front-end to postgres95
5  *
6  * Copyright (c) 1996, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *    $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.2 1996/07/16 06:37:28 scrappy Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>
15 #include <string.h>
16 #include <signal.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20
21 #include "libpq-fe.h"
22 #include "stringutils.h"
23
24 #include "psqlHelp.h"
25
26 #ifdef NOREADLINE
27 extern char *readline(char *);  /* in rlstubs.c */
28 #else
29 /* from the GNU readline library */
30 #ifdef OLD_READLINE
31 #include "readline.h"
32 #include "history.h"
33 #else
34 #include <readline/readline.h>
35 #include <history.h>
36 #endif
37 #endif
38
39 #define MAX_QUERY_BUFFER 20000
40 #define MAX_FIELD_SEP_LENGTH 40
41
42 #define COPYBUFSIZ      8192
43
44 #define DEFAULT_FIELD_SEP " "
45 #define DEFAULT_EDITOR  "vi"
46 #define DEFAULT_SHELL  "/bin/sh"
47
48 typedef struct _psqlSettings {
49     int echoQuery;        /* if 1, echo the query before sending it */
50     int quiet;            /* run quietly, no messages, no promt */
51     int singleStep;       /* if 1, prompt for each query */ 
52     int singleLineMode;   /* if 1, query terminated by newline */
53     int useReadline;      /* use the readline routines or not */
54     int printHeader;      /* print output field headers or not */
55     int fillAlign;        /* fill align the fields */
56     FILE *queryFout;      /* where to send the query results */
57     char fieldSep[MAX_FIELD_SEP_LENGTH];  /* field separator */
58 } PsqlSettings;
59
60 /* declarations for functions in this file */
61 static void usage(char* progname);
62 static void slashUsage();
63 static void handleCopyOut(PGresult *res, int quiet);
64 static void handleCopyIn(PGresult *res, int quiet);
65 static int tableList(PGconn* conn, int deep_tablelist);
66 static int tableDesc(PGconn* conn, char* table);
67
68 char* gets_noreadline(char* prompt, FILE* source);
69 char* gets_readline(char* prompt, FILE* source);
70 char* gets_fromFile(char* prompt, FILE* source);
71 int listAllDbs(PGconn *db, PsqlSettings *settings);
72 int SendQuery(PGconn* db, char* query, PsqlSettings *settings);
73 int HandleSlashCmds(PGconn** db_ptr,  
74                     char *line,
75                     char** prompt_ptr,
76                     char *query,
77                     PsqlSettings *settings);
78 int MainLoop(PGconn** db_ptr, FILE *source, PsqlSettings *settings);
79 FILE* setFout(char *fname);
80
81
82 /*
83  * usage 
84  *   print out usage for command line arguments 
85  */
86
87 static void  
88 usage(char* progname)
89 {
90   fprintf(stderr,"Usage: %s [options] [dbname]\n",progname);
91   fprintf(stderr,"\t -a authsvc              set authentication service\n"); 
92   fprintf(stderr,"\t -A                      turn off fill-justification when printing out attributes\n");
93   fprintf(stderr,"\t -c query                run single query (slash commands too)\n");
94   fprintf(stderr,"\t -d dbName               specify database name\n");
95   fprintf(stderr,"\t -e                      echo the query sent to the backend\n");
96   fprintf(stderr,"\t -f filename             use file as a source of queries\n");
97   fprintf(stderr,"\t -F sep                  set the field separator (default is " ")\n");
98   fprintf(stderr,"\t -h                      help information\n");
99   fprintf(stderr,"\t -H host                 set database server host\n");
100   fprintf(stderr,"\t -l                      list available databases\n");
101   fprintf(stderr,"\t -n                      don't use readline library\n");
102   fprintf(stderr,"\t -o filename             send output to filename\n");
103   fprintf(stderr,"\t -p port                 set port number\n");
104   fprintf(stderr,"\t -q                      run quietly (no messages, no prompts)\n");
105   fprintf(stderr,"\t -s                      single step mode (prompts for each query)\n");
106   fprintf(stderr,"\t -S                      single line mode (i.e. query terminated by newline)\n");
107   fprintf(stderr,"\t -T                      turn off printing of attribute names\n");
108   exit(1);
109 }
110
111 /*
112  * slashUsage
113  *    print out usage for the backslash commands 
114  */
115
116 static void
117 slashUsage()
118 {
119   fprintf(stderr,"\t \\a           -- toggle fill-justification of display of attributes\n");
120   fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>\n");
121   fprintf(stderr,"\t \\d *         -- list tables in database and columns in all tables\n");
122   fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>\n");
123   fprintf(stderr,"\t \\f <sep>     -- change field separator\n");
124   fprintf(stderr,"\t \\g           -- query to backend\n");
125   fprintf(stderr,"\t \\h <command> -- help on syntax of sql commands\n");
126   fprintf(stderr,"\t \\h *         -- complete description of all sql commands\n");
127   fprintf(stderr,"\t \\g           -- send query to backend\n");
128   fprintf(stderr,"\t \\i <fname>   -- read queries from filename\n");
129   fprintf(stderr,"\t \\l           -- list all databases\n");
130   fprintf(stderr,"\t \\o [<fname>] -- send query results file named <fname> or stdout\n");
131   fprintf(stderr,"\t \\p           -- print the current query buffer\n");
132   fprintf(stderr,"\t \\q           -- quit\n");
133   fprintf(stderr,"\t \\s [<fname>] -- save or print history\n");
134   fprintf(stderr,"\t \\t           -- toggle output field headers (defaults to on)\n");
135   fprintf(stderr,"\t \\! [<cmd>]   -- shell escape\n");
136   fprintf(stderr,"\t \\?           -- help\n");
137 }
138
139 /*
140  * listAllDbs
141  *
142  * list all the databases in the system
143  *     returns 0 if all went well
144  *    
145  *
146  */
147 int 
148 listAllDbs(PGconn *db, PsqlSettings *settings)
149 {
150   PGresult *results;
151   char* query = "select * from pg_database;";
152
153   results = PQexec(db, query);
154   if (results == NULL) {
155     fprintf(stderr,"%s", PQerrorMessage(db));
156     return 1;
157   }
158
159   if (PQresultStatus(results) != PGRES_TUPLES_OK)
160     {
161       fprintf(stderr,"Unexpected error from executing: %s\n", query);
162       return 2;
163     }
164   else
165     {
166       PQdisplayTuples(results,
167                       settings->queryFout, 
168                       settings->fillAlign,
169                       settings->fieldSep,
170                       settings->printHeader,
171                       settings->quiet);
172       PQclear(results);
173       return 0;
174     }
175 }
176
177 /*
178  * tableList (PGconn* conn)
179  *
180  * List The Database Tables
181  *     returns 0 if all went well
182  *    
183  */
184 int
185 tableList (PGconn* conn, int deep_tablelist)
186 {
187   char listbuf[256];
188   int nColumns; 
189   int i;
190   char* ru;
191   char* rk;
192   char* rr;
193
194   PGresult* res;
195
196   listbuf[0] = '\0';
197   strcat(listbuf,"SELECT usename, relname, relkind, relhasrules");
198   strcat(listbuf,"  FROM pg_class, pg_user ");
199   strcat(listbuf,"WHERE ( relkind = 'r' OR relkind = 'i') ");
200   strcat(listbuf,"  and relname !~ '^pg_'");
201   strcat(listbuf,"  and relname !~ '^Inv'");
202 /* the usesysid = relowner won't work on stock 1.0 dbs, need to 
203    add in the int4oideq function */
204   strcat(listbuf,"  and usesysid = relowner");
205   strcat(listbuf,"  ORDER BY relname ");
206   res = PQexec(conn,listbuf);
207   if (res == NULL) {
208       fprintf(stderr,"%s", PQerrorMessage(conn));
209       return (-1);
210   }
211
212   if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
213       fprintf(stderr,"No tables found in database %s.\n", PQdb(conn));
214       PQclear(res);
215       return (-1);
216   }
217
218   /* first, print out the attribute names */
219   nColumns = PQntuples(res);
220   if (nColumns > 0)
221   {
222       if ( deep_tablelist ) {
223           /* describe everything here */
224           char **table;
225           table = (char**)malloc(nColumns * sizeof(char*));
226           if ( table == NULL )
227               perror("malloc");
228           
229           /* load table table*/
230           for (i=0; i < nColumns; i++) {
231               table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1);
232               if ( table[i] == NULL )
233                   perror("malloc");
234               strcpy(table[i],PQgetvalue(res,i,1));
235           }
236
237         PQclear(res);
238         for (i=0; i < nColumns; i++) {
239            tableDesc(conn,table[i]);
240         }
241         free(table);
242       }
243       else {
244         /* Display the information */
245
246         printf ("\nDatabase    = %s\n", PQdb(conn));
247         printf (" +------------------+----------------------------------+----------+\n");
248         printf (" |  Owner           |             Relation             |   Type   |\n");
249         printf (" +------------------+----------------------------------+----------+\n");
250
251         /* next, print out the instances */
252         for (i=0; i < PQntuples(res); i++) {
253             printf (" | %-16.16s", PQgetvalue(res,i,0));
254             printf (" | %-32.32s | ", PQgetvalue(res,i,1));
255             rk =  PQgetvalue(res,i,2);
256             rr =  PQgetvalue(res,i,3);
257             if (strcmp(rk, "r") == 0)
258                 printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" );
259             else
260                 printf ("%-8.8s |", "index");
261             printf("\n");
262         }
263         printf (" +------------------+----------------------------------+----------+\n");
264         PQclear(res);
265       }
266       return (0);
267   
268   } else {
269     fprintf (stderr, "Couldn't find any tables!\n");
270     return (-1);
271   }
272 }
273
274 /*
275  * Describe a table   (PGconn* conn, char* table)
276  *
277  * Describe the columns in a database table.
278  *     returns 0 if all went well
279  *    
280  *
281  */
282 int
283 tableDesc (PGconn* conn, char* table)
284 {
285   char descbuf[256];
286   int nColumns;
287   char *rtype;
288   int i;
289   int rsize;
290
291   PGresult* res;
292
293   /* Build the query */
294
295   descbuf[0] = '\0';
296   strcat(descbuf,"SELECT a.attnum, a.attname, t.typname, a.attlen");
297   strcat(descbuf,"  FROM pg_class c, pg_attribute a, pg_type t ");
298   strcat(descbuf,"    WHERE c.relname = '");
299   strcat(descbuf,table);
300   strcat(descbuf,"'");
301   strcat(descbuf,"    and a.attnum > 0 ");
302   strcat(descbuf,"    and a.attrelid = c.oid ");
303   strcat(descbuf,"    and a.atttypid = t.oid ");
304   strcat(descbuf,"  ORDER BY attnum ");
305   res = PQexec(conn,descbuf);
306   if (res == NULL) {
307     fprintf(stderr,"%s", PQerrorMessage(conn));
308     return (-1);
309    }
310  if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
311    fprintf(stderr,"Couldn't find table %s!\n", table);
312    PQclear(res);
313    return (-1);
314  }
315   /* first, print out the attribute names */
316   nColumns = PQntuples(res);
317   if (nColumns > 0)
318   {
319     /*
320     ** Display the information
321     */
322
323     printf ("\nTable    = %s\n", table);
324     printf ("+----------------------------------+----------------------------------+-------+\n");
325     printf ("|              Field               |              Type                | Length|\n");
326     printf ("+----------------------------------+----------------------------------+-------+\n");
327
328     /* next, print out the instances */
329     for (i=0; i < PQntuples(res); i++) {
330       printf ("| %-32.32s | ", PQgetvalue(res,i,1));
331       rtype = PQgetvalue(res,i,2);
332       rsize = atoi(PQgetvalue(res,i,3));
333       if (strcmp(rtype, "text") == 0) {
334         printf ("%-32.32s |", rtype);
335         printf (" %-6s |",  "var" );
336       }
337       else if (strcmp(rtype, "bpchar") == 0) {
338         printf ("%-32.32s |", "char");
339         printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
340       }
341       else if (strcmp(rtype, "varchar") == 0) {
342         printf ("%-32.32s |", rtype);
343         printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
344       }
345       else {
346           /* array types start with an underscore */
347           if (rtype[0] != '_')
348               printf ("%-32.32s |", rtype);
349           else  {
350               char *newname;
351               newname = malloc(strlen(rtype) + 2);
352               strcpy(newname, rtype+1);
353               strcat(newname, "[]");
354               printf ("%-32.32s |", newname);
355               free(newname);
356           }
357         if (rsize > 0) 
358             printf ("%-6i |", rsize);
359         else
360             printf ("%-6s |", "var");
361       }
362       printf("\n");
363     }
364     printf ("+----------------------------------+----------------------------------+-------+\n");
365
366     PQclear(res);
367     return (0);
368   
369   } else {
370       fprintf (stderr, "Couldn't find table %s!\n", table);
371     return (-1);
372   }
373 }
374
375 typedef char* (*READ_ROUTINE)(char* prompt, FILE* source);
376
377 /* gets_noreadline  prompt source
378       gets a line of input without calling readline, the source is ignored
379 */
380 char* 
381 gets_noreadline(char* prompt, FILE* source)
382 {
383     fputs(prompt, stdout);
384     fflush(stdout);
385     return(gets_fromFile(prompt,stdin));
386 }
387
388 /*
389  * gets_readline  prompt source
390  *   the routine to get input from GNU readline(), the source is ignored 
391  * the prompt argument is used as the prompting string
392  */
393 char* 
394 gets_readline(char* prompt, FILE* source)
395 {
396   return (readline(prompt));
397 }
398
399
400 /*
401  * gets_fromFile  prompt source
402  *    
403  * the routine to read from a file, the prompt argument is ignored
404  * the source argument is a FILE* 
405  */
406 char* 
407 gets_fromFile(char* prompt, FILE* source)
408 {
409   char* line;
410   int len;
411
412   line = malloc(MAX_QUERY_BUFFER+1);
413
414   /* read up to MAX_QUERY_BUFFER characters */
415   if (fgets(line, MAX_QUERY_BUFFER, source) == NULL)
416     return NULL;
417
418   line[MAX_QUERY_BUFFER-1] = '\0';
419   len = strlen(line);
420   if (len == MAX_QUERY_BUFFER)
421     {
422       fprintf(stderr, "line read exceeds maximum length.  Truncating at %d\n", MAX_QUERY_BUFFER);
423     }
424   
425   return line;
426 }
427
428 /*
429  * SendQuery:
430      SendQuery: send the query string to the backend 
431  *
432  *  return 0 if the query executed successfully
433  *  returns 1 otherwise
434  */
435 int
436 SendQuery(PGconn* db, char* query, PsqlSettings *settings)
437 {
438   PGresult* results;
439   PGnotify* notify;
440   int status = 0;
441
442   if (settings->singleStep)
443         fprintf(stdout, "\n*******************************************************************************\n");
444
445   if (settings->echoQuery || settings->singleStep) {
446       fprintf(stderr,"QUERY: %s\n",query);
447       fflush(stderr);
448   }
449
450   if (settings->singleStep) {
451         fprintf(stdout, "\n*******************************************************************************\n");
452         fflush(stdout);
453         printf("\npress return to continue ..\n");
454         gets_fromFile("",stdin);
455   }
456
457   results = PQexec(db, query);
458   if (results == NULL) {
459     fprintf(stderr,"%s",PQerrorMessage(db));
460     return 1;
461   }
462
463   switch (PQresultStatus(results)) {
464   case PGRES_TUPLES_OK:
465       PQdisplayTuples(results,
466                       settings->queryFout,
467                       settings->fillAlign,
468                       settings->fieldSep,
469                       settings->printHeader,
470                       settings->quiet);
471       PQclear(results);
472       break;
473   case PGRES_EMPTY_QUERY:
474     /* do nothing */
475     break;
476   case PGRES_COMMAND_OK:
477     if (!settings->quiet)
478       fprintf(stdout,"%s\n",PQcmdStatus(results));
479     break;
480   case PGRES_COPY_OUT:
481     handleCopyOut(results, settings->quiet);
482     break;
483   case PGRES_COPY_IN:
484     handleCopyIn(results, settings->quiet);
485     break;
486   case PGRES_NONFATAL_ERROR:
487   case PGRES_FATAL_ERROR:
488   case PGRES_BAD_RESPONSE:
489     status = 1;
490     fprintf(stderr,"%s",PQerrorMessage(db));
491     break;
492
493   } 
494
495   /* check for asynchronous returns */
496   notify = PQnotifies(db);
497   if (notify) {
498       fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
499               notify->relname, notify->be_pid);
500       free(notify);
501   }
502
503   return status;
504
505 }
506
507 /*
508   HandleSlashCmds:
509
510   Handles all the different commands that start with \ 
511      db_ptr is a pointer to the TgDb* structure
512      line is the current input line
513      prompt_ptr is a pointer to the prompt string,
514                   a pointer is used because the prompt can be used with 
515                   a connection to a new database
516   returns a status:
517        0 - send currently constructed query to backend (i.e. we got a \g)
518        1 - skip processing of this line, continue building up query
519        2 - terminate processing of this query entirely
520 */
521 int
522 HandleSlashCmds(PGconn** db_ptr, 
523                 char* line, 
524                 char** prompt_ptr, 
525                 char *query,
526                 PsqlSettings *settings)
527 {
528   int status = 0;
529   PGconn* db = *db_ptr;
530   char* dbname = PQdb(db);
531   char *optarg = NULL;
532   int len;
533
534   len = strlen(line);
535   if (len > 2)
536       optarg = leftTrim(line+2);
537   switch (line[1])
538     {
539     case 'a': /* toggles to fill fields on output */
540         if (settings->fillAlign)
541             settings->fillAlign = 0;
542         else 
543             settings->fillAlign = 1;
544         if (!settings->quiet)
545             fprintf(stderr,"turning %s fill-justification\n",
546                     (settings->fillAlign) ? "on" : "off" );
547         break;
548     case 'c':  /* \c means connect to new database */
549       {
550           if (!optarg) {
551               fprintf(stderr,"\\c must be followed by a database name\n");
552               status = 1;
553               break;
554           }
555           if (strcmp(optarg, dbname) == 0)  {
556               fprintf(stderr,"already connected to %s\n", dbname);
557               status = 1;
558               break;
559           }
560           else {
561               PGconn *olddb;
562               
563               printf("closing connection to database:%s\n", dbname);
564               olddb = db;
565               db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg);
566               *db_ptr = db;
567               printf("connecting to new database: %s\n", optarg);
568               if (PQstatus(db) == CONNECTION_BAD) {
569                   fprintf(stderr,"%s\n", PQerrorMessage(db));
570                   printf("reconnecting to %s\n", dbname);
571                   db = PQsetdb(PQhost(olddb), PQport(olddb), 
572                                NULL, NULL, dbname);
573                   *db_ptr = db;
574                   if (PQstatus(db) == CONNECTION_BAD) {
575                       fprintf(stderr, 
576                               "could not reconnect to %s.  exiting\n", dbname);
577                       exit(2);
578                   }
579                   status = 1;
580                   break;
581               }
582               PQfinish(olddb);
583               free(*prompt_ptr);
584               *prompt_ptr = malloc(strlen(optarg) + 10);
585               sprintf(*prompt_ptr,"%s=> ", optarg);
586               status = 1;
587             break;
588           }
589       }
590       break;
591     case 'd':     /* \d describe tables or columns in a table */
592       {
593         if (!optarg) {
594           tableList(db,0);
595           status = 1;
596           break;
597         }
598         if ( strcmp(optarg,"*") == 0 ) {
599            tableList(db, 0);
600            tableList(db, 1);
601         }
602         else {
603            tableDesc(db,optarg);
604         }
605         status = 1;
606         break;
607       }
608     case 'e':
609       {
610         char s[256];
611         int fd;
612         int ql = strlen(query);
613         int f_arg = 0;
614         int cc;
615         if (optarg)
616         {
617                 f_arg = 1;
618                 strcpy(s, optarg);
619         }
620         else
621         {
622                 sprintf(s, "/tmp/psql.%d.%d", getuid(), getpid());
623                 unlink(s);
624                 if (ql)
625                 {
626                         if ((fd=open(s, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1)
627                         {
628                                 perror(s);
629                                 break;
630                         }
631                         if (query[ql-1]!='\n')
632                                 strcat(query, "\n");
633                         if (write(fd, query, ql)!=ql)
634                         {
635                                 perror(s);
636                                 close(fd);
637                                 unlink(s);
638                                 break;
639                         }
640                         close(fd);
641                 }
642         }
643         {
644             char sys[256];
645             char *editorName;
646             editorName = getenv("EDITOR");
647             if (editorName == NULL)
648                 editorName = DEFAULT_EDITOR;
649             sprintf(sys, "exec %s %s", editorName, s);
650             system(sys);
651         }
652         if ((fd=open(s, O_RDONLY))==-1)
653         {
654                 if (!f_arg)
655                         unlink(s);
656                 break;
657         }
658         if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1)
659         {
660                 perror(s);
661                 close(fd);
662                 if (!f_arg)
663                         unlink(s);
664                 break;
665         }       
666         query[cc]='\0';
667         close(fd);
668         if (!f_arg)
669                 unlink(s);
670         rightTrim(query);
671         if (query[strlen(query)-1]==';')
672                 return 0;
673         break;
674       }
675   case 'f':
676       if (optarg)
677           strcpy(settings->fieldSep,optarg);
678       else
679           strcpy(settings->fieldSep,DEFAULT_FIELD_SEP);
680       break;
681   case 'g':  /* \g means send query */
682       status = 0;     
683         break;
684     case 'i':     /* \i is include file */
685       {
686         FILE* fd;
687
688         if (!optarg) {
689           fprintf(stderr,"\\i must be followed by a file name\n");
690           status = 1;
691           break;
692         }
693
694         if ( (fd = fopen(optarg, "r")) == NULL)
695           {
696             fprintf(stderr,"file named %s could not be opened\n",optarg);
697             status = 1;
698             break;
699           }
700         MainLoop(&db, fd, settings);
701         fclose(fd);
702         status = 1;
703         break;
704       }
705     case 'h':
706       {
707         char* cmd;
708         int i, numCmds;
709         int all_help = 0;
710
711         if (!optarg) {
712             printf("type \\h <cmd> where <cmd> is one of the following:\n");
713             i = 0;
714             while (QL_HELP[i].cmd != NULL)
715               {
716                 printf("\t%s\n", QL_HELP[i].cmd);
717                 i++;
718               }
719              printf("type \\h * for a complete description of all commands\n");
720           }
721         else
722           {
723           cmd = optarg;
724
725           numCmds = 0;
726           while (QL_HELP[numCmds++].cmd != NULL);
727
728           numCmds = numCmds - 1;
729
730           if ( strcmp(cmd,"*") == 0 ) {
731              all_help=1;
732           }
733
734           for (i=0; i<numCmds;i++)  {
735               if (strcmp(QL_HELP[i].cmd, cmd) == 0 || all_help)    {
736                 printf("Command: %s\n",QL_HELP[i].cmd);
737                 printf("Description: %s\n", QL_HELP[i].help);
738                 printf("Syntax:\n");
739                 printf("%s\n", QL_HELP[i].syntax);
740                 if ( all_help ) {
741                    printf("\n");
742                 }
743                 else {
744                    break;
745                }
746             }
747           }
748           if (i == numCmds && ! all_help)
749             printf("command not found,  try \\h with no arguments to see available help\n");
750         }
751         status = 1;
752         break;
753       }
754     case 'l':     /* \l is list database */
755       listAllDbs(db,settings);
756       status = 1;
757       break;
758     case 'o':
759       settings->queryFout = setFout(optarg);
760       break;
761     case 'p':
762         if (query) {
763             fputs(query, stdout);
764             fputc('\n', stdout);
765             status = 1;
766         }
767         break;
768     case 'r':
769         query[0] = '\0';
770         status = 1;
771         break;          
772     case 'q': /* \q is quit */
773       status = 2;
774       break;
775     case 's': /* \s is save history to a file */
776       {
777         char* fname;
778
779         if (!optarg) {
780           fprintf(stderr,"\\s must be followed by a file name\n");
781           status = 1;
782           break;
783         }
784
785         fname = optarg;
786         if (write_history(fname) != 0)
787           {
788             fprintf(stderr,"cannot write history to %s\n",fname);
789           }
790         status = 1;
791         break;
792       }
793     case 't':
794         if ( settings->printHeader )
795              settings->printHeader = 0;
796         else
797              settings->printHeader = 1;
798         if (!settings->quiet)
799             fprintf(stderr,"turning %s printing of field headers\n",
800                     (settings->printHeader) ? "on" : "off" );
801         break;
802     case '!':
803       if (!optarg) {
804           char sys[256];
805           char *shellName;
806           shellName = getenv("SHELL");
807           if (shellName == NULL) 
808               shellName = DEFAULT_SHELL;
809           sprintf(sys,"exec %s", shellName);
810           system(sys);
811       }
812       else
813           system(optarg);
814       break;
815     default:
816     case '?':     /* \? is help */
817       slashUsage();
818       status = 1;
819       break;
820     }
821   return status;
822 }
823
824 /* 
825  MainLoop: main processing loop for reading lines of input
826  and sending them to the backend
827
828  this loop is re-entrant.  May be called by \i command
829  which reads input from a file
830
831  *db_ptr must be initialized and set
832 */
833 int
834 MainLoop(PGconn** db_ptr, 
835          FILE* source,
836          PsqlSettings *settings)
837 {
838   char* prompt;                 /* readline prompt */
839   char* line;                   /* line of input*/
840   int len;                      /* length of the line */
841   char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
842   PGconn* db = *db_ptr;
843   char* dbname = PQdb(db);
844   int exitStatus = 0;
845
846   int slashCmdStatus = 0;
847  /* slashCmdStatus can be:
848        0 - send currently constructed query to backend (i.e. we got a \g)
849        1 - skip processing of this line, continue building up query
850        2 - terminate processing of this query entirely
851   */
852
853   int send_query = 0;
854   int interactive;
855   READ_ROUTINE GetNextLine;
856
857   interactive = (source == stdin);
858
859   if (interactive) {
860     prompt = malloc(strlen(dbname) + 10);
861     if (settings->quiet)
862       prompt[0] = '\0';
863     else
864       sprintf(prompt,"%s=> ", dbname);
865     if (settings->useReadline) {
866         using_history();
867         GetNextLine = gets_readline;
868     } else
869         GetNextLine = gets_noreadline;
870
871   }
872   else
873     GetNextLine = gets_fromFile;
874
875   query[0] = '\0';
876   
877   /* main loop for getting queries and executing them */
878   while ((line = GetNextLine(prompt, source)) != NULL)
879     {
880         exitStatus = 0;
881       line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */
882
883       if (line[0] == '\0') {
884           free(line);
885           continue;
886       }
887
888       /* filter out comment lines that begin with --,
889          this could be incorrect if -- is part of a quoted string.
890          But we won't go through the trouble of detecting that.  If you have
891          -- in your quoted string, be careful and don't start a line with it*/
892       if (line[0] == '-' && line[1] == '-') {
893           if (settings->singleStep) /* in single step mode, show comments */
894               fprintf(stdout,"%s\n",line);
895           free(line);
896           continue;
897       }
898
899       len = strlen(line);
900
901       if (interactive && settings->useReadline)
902           add_history(line);      /* save non-empty lines in history */
903       
904       /* do the query immediately if we are doing single line queries 
905        or if the last character is a semicolon */
906       send_query = settings->singleLineMode || (line[len-1] == ';') ;
907
908       /* normally, \ commands have to be start the line,
909          but for backwards compatibility with monitor,
910          check for \g at the end of line */
911       if (len > 2 && !send_query) 
912         {
913           if (line[len-1]=='g' && line[len-2]=='\\')
914             {
915             send_query = 1;
916             line[len-2]='\0';
917           }
918         }
919       
920       /* slash commands have to be on their own line */
921       if (line[0] == '\\') {
922           slashCmdStatus = HandleSlashCmds(db_ptr, 
923                                            line, 
924                                            &prompt, 
925                                            query,
926                                            settings);
927         db = *db_ptr; /* in case \c changed the database */
928         if (slashCmdStatus == 1)
929           continue;
930         if (slashCmdStatus == 2)
931           break;
932         if (slashCmdStatus == 0)
933           send_query = 1;
934       }
935       else
936         if (strlen(query) + len > MAX_QUERY_BUFFER)
937           {
938             fprintf(stderr,"query buffer max length of %d exceeded\n",MAX_QUERY_BUFFER);
939             fprintf(stderr,"query line ignored\n");
940           }
941       else
942         if (query[0]!='\0') {
943             strcat(query,"\n");
944             strcat(query,line);
945         }
946       else
947         strcpy(query,line);
948       
949       if (send_query && query[0] != '\0')
950         {
951             /* echo the line read from the file,
952              unless we are in single_step mode, because single_step mode
953              will echo anyway */
954           if (!interactive && !settings->singleStep) 
955             fprintf(stderr,"%s\n",query);
956
957           exitStatus = SendQuery(db, query, settings);
958           query[0] = '\0';
959         }
960       
961        free(line); /* free storage malloc'd by GetNextLine */
962     } /* while */
963   return exitStatus;
964
965
966 int
967 main(int argc, char** argv)
968 {
969   extern char* optarg;
970   extern int optind, opterr;
971   
972   PGconn *db;
973   char* dbname = NULL;
974   char* host = NULL;
975   char* port = NULL;
976   char* qfilename = NULL;
977   char errbuf[ERROR_MSG_LENGTH];
978
979   PsqlSettings settings;
980
981   char* singleQuery = NULL;
982
983   int listDatabases = 0 ;
984   int exitStatus = 0;
985   int singleSlashCmd = 0;
986   int c;
987
988
989 #ifdef NOREADLINE
990   settings.useReadline = 0;
991 #else
992   settings.useReadline = 1;
993 #endif
994
995   settings.quiet = 0;
996   settings.fillAlign = 1;
997   settings.printHeader = 1;
998   settings.echoQuery = 0;
999   settings.singleStep = 0;
1000   settings.singleLineMode = 0;
1001   settings.queryFout = stdout;
1002   strcpy(settings.fieldSep, DEFAULT_FIELD_SEP);
1003
1004   while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lhH:nso:p:qST")) != EOF) {
1005     switch (c) {
1006     case 'A':
1007         settings.fillAlign = 0;
1008         break;
1009     case 'a':
1010         fe_setauthsvc(optarg, errbuf);
1011         break;
1012     case 'c':
1013         singleQuery = optarg;
1014         if ( singleQuery[0] == '\\' ) {
1015             singleSlashCmd=1;
1016         }
1017         break;
1018     case 'd':
1019       dbname = optarg;
1020       break;
1021     case 'e':
1022       settings.echoQuery = 1;
1023       break;
1024     case 'f':
1025       qfilename = optarg;
1026       break;
1027     case 'F':
1028         strncpy(settings.fieldSep,optarg,MAX_FIELD_SEP_LENGTH); 
1029         break;
1030     case 'l':
1031       listDatabases = 1;
1032       break;
1033     case 'H':
1034       host = optarg;
1035       break;
1036     case 'n':
1037         settings.useReadline = 0;
1038         break;
1039     case 'o':
1040         settings.queryFout = setFout(optarg);
1041         break;
1042     case 'p':
1043       port = optarg;
1044       break;
1045     case 'q':
1046       settings.quiet = 1;
1047       break;
1048     case 's':
1049       settings.singleStep = 1;
1050       break;
1051     case 'S':
1052       settings.singleLineMode = 1;
1053       break;
1054     case 'T':
1055         settings.printHeader = 0;
1056         break;
1057     case 'h':
1058     default:
1059       usage(argv[0]);
1060       break;
1061     }
1062   }
1063   /* if we still have an argument, use it as the database name */
1064   if (argc - optind == 1)
1065     dbname = argv[optind];
1066
1067   if (listDatabases)
1068     dbname = "template1";
1069   
1070   db = PQsetdb(host, port, NULL, NULL, dbname);
1071   dbname = PQdb(db);
1072
1073   if (PQstatus(db) == CONNECTION_BAD) {
1074     fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
1075     fprintf(stderr,"%s",PQerrorMessage(db));
1076     exit(1);
1077   }
1078   if (listDatabases) {
1079       exit(listAllDbs(db,&settings));
1080     }
1081
1082   if (!settings.quiet && !singleQuery && !qfilename) {
1083     printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
1084     printf("  Please read the file COPYRIGHT for copyright terms of POSTGRES95\n\n");
1085     printf("   type \\? for help on slash commands\n");
1086     printf("   type \\q to quit\n");
1087     printf("   type \\g or terminate with semicolon to execute query\n");
1088     printf(" You are currently connected to the database: %s\n\n", dbname);
1089      }
1090
1091   if (qfilename || singleSlashCmd) {
1092       /* read in a file full of queries instead of reading in queries
1093          interactively */
1094       char *line;
1095       char prompt[100];
1096
1097       if ( singleSlashCmd ) {
1098         /* Not really a query, but "Do what I mean, not what I say." */
1099         line = singleQuery;
1100       }
1101       else {
1102         line = malloc(strlen(qfilename) + 5);
1103         sprintf(line,"\\i %s", qfilename);
1104       }
1105       HandleSlashCmds(&db, line, (char**)prompt, "", &settings);
1106       
1107    } else {
1108        if (singleQuery) {
1109            exitStatus = SendQuery(db, singleQuery, &settings);
1110        }
1111        else 
1112            exitStatus = MainLoop(&db, stdin, &settings);
1113    }
1114
1115   PQfinish(db);
1116
1117   return exitStatus;
1118 }
1119
1120
1121 static void
1122 handleCopyOut(PGresult *res, int quiet)
1123 {
1124     bool copydone = false;
1125     char copybuf[COPYBUFSIZ];
1126     int ret;
1127
1128     if (!quiet)
1129         fprintf(stdout, "Copy command returns...\n");
1130     
1131     while (!copydone) {
1132         ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
1133         
1134         if (copybuf[0] == '.' && copybuf[1] =='\0') {
1135             copydone = true;    /* don't print this... */
1136         } else {
1137             fputs(copybuf, stdout);
1138             switch (ret) {
1139             case EOF:
1140                 copydone = true;
1141                 /*FALLTHROUGH*/
1142             case 0:
1143                 fputc('\n', stdout);
1144                 break;
1145             case 1:
1146                 break;
1147             }
1148         }
1149     }
1150     fflush(stdout);
1151     PQendcopy(res->conn);
1152 }
1153
1154
1155 static void
1156 handleCopyIn(PGresult *res, int quiet)
1157 {
1158     bool copydone = false;
1159     bool firstload;
1160     bool linedone;
1161     char copybuf[COPYBUFSIZ];
1162     char *s;
1163     int buflen;
1164     int c;
1165     
1166     if (!quiet) {
1167         fputs("Enter info followed by a newline\n", stdout);
1168         fputs("End with a dot on a line by itself.\n", stdout);
1169     }
1170     
1171     /*
1172      * eat extra newline still in input buffer
1173      *
1174      */
1175     fflush(stdin);
1176     if ((c = getc(stdin)) != '\n' && c != EOF) {
1177         (void) ungetc(c, stdin);
1178     }
1179     
1180     while (!copydone) {                 /* for each input line ... */
1181         if (!quiet) {
1182             fputs(">> ", stdout);
1183             fflush(stdout);
1184         }
1185         firstload = true;
1186         linedone = false;
1187         while (!linedone) {             /* for each buffer ... */
1188             s = copybuf;
1189             buflen = COPYBUFSIZ;
1190             for (; buflen > 1 &&
1191                  !(linedone = (c = getc(stdin)) == '\n' || c == EOF);
1192                  --buflen) {
1193                 *s++ = c;
1194             }
1195             if (c == EOF) {
1196                 /* reading from stdin, but from a file */
1197                 PQputline(res->conn, ".");
1198                 copydone = true;
1199                 break;
1200             }
1201             *s = '\0';
1202             PQputline(res->conn, copybuf);
1203             if (firstload) {
1204                 if (!strcmp(copybuf, ".")) {
1205                     copydone = true;
1206                 }
1207                 firstload = false;
1208             }
1209         }
1210         PQputline(res->conn, "\n");
1211     }
1212     PQendcopy(res->conn);
1213 }
1214
1215
1216 /* try to open fname and return a FILE*,
1217    if it fails, use stdout, instead */
1218 FILE* 
1219 setFout(char *fname)
1220 {
1221     FILE *queryFout;
1222
1223     if (!fname)
1224         queryFout = stdout;
1225     else {
1226         queryFout = fopen(fname, "w");
1227         if (!queryFout) {
1228             perror(fname);
1229             queryFout = stdout;
1230         }
1231     }
1232
1233     return queryFout;
1234 }
1235