-/*-------------------------------------------------------------------------
+*-------------------------------------------------------------------------
*
* psql.c--
* an interactive front-end to postgres95
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.23 1996/10/14 00:33:47 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.24 1996/11/04 09:17:55 bryanh Exp $
*
*-------------------------------------------------------------------------
*/
#include "psqlHelp.h"
#ifdef NOREADLINE
-extern char *readline(char *); /* in rlstubs.c */
+extern char *readline(char *); /* in rlstubs.c */
#else
/* from the GNU readline library */
#ifdef OLD_READLINE
#define MAX_QUERY_BUFFER 20000
-#define COPYBUFSIZ 8192
+#define COPYBUFSIZ 8192
#define DEFAULT_FIELD_SEP "|"
#define DEFAULT_EDITOR "vi"
#define DEFAULT_SHELL "/bin/sh"
typedef struct _psqlSettings {
- PGconn *db; /* connection to backend */
+ PGconn *db; /* connection to backend */
FILE *queryFout; /* where to send the query results */
PQprintOpt opt; /* options to be passed to PQprint */
- char *prompt; /* prompt to display */
- char *gfname; /* one-shot file output argument for \g */
- bool notty; /* input or output is not a tty */
- bool pipe; /* queryFout is from a popen() */
+ char *prompt; /* prompt to display */
+ char *gfname; /* one-shot file output argument for \g */
+ bool notty; /* input or output is not a tty */
+ bool pipe; /* queryFout is from a popen() */
bool echoQuery; /* echo the query before sending it */
bool quiet; /* run quietly, no messages, no promt */
bool singleStep; /* prompt before for each query */
/* declarations for functions in this file */
static void usage(char *progname);
static void slashUsage();
-static void handleCopyOut(PGresult *res, bool quiet);
-static void handleCopyIn(PGresult *res, bool quiet);
+static void handleCopyOut(PGresult *res, bool quiet, FILE *copystream);
+static void handleCopyIn(PGresult *res, const bool mustprompt,
+ FILE *copystream);
static int tableList(PsqlSettings *ps, bool deep_tablelist);
static int tableDesc(PsqlSettings *ps, char *table);
char *gets_readline(char *prompt, FILE *source);
char *gets_fromFile(char *prompt, FILE *source);
int listAllDbs(PsqlSettings *settings);
-int SendQuery(PsqlSettings *settings, char *query);
+void SendQuery(bool *success_p, PsqlSettings *settings, const char *query,
+ const bool copy_in, const bool copy_out, FILE *copystream);
int HandleSlashCmds(PsqlSettings *settings,
- char *line,
- char *query);
+ char *line,
+ char *query);
int MainLoop(PsqlSettings *settings, FILE *source);
/* probably should move this into libpq */
void PQprint(FILE *fp,
PGresult *res,
- PQprintOpt *po
- );
+ PQprintOpt *po
+ );
FILE *setFout(PsqlSettings *ps, char *fname);
char *on(bool f)
{
- return f? "on": "off";
+ return f? "on": "off";
}
static void
fprintf(stderr,"\t \\? -- help\n");
fprintf(stderr,"\t \\a -- toggle field-alignment (currenty %s)\n", on(ps->opt.align));
fprintf(stderr,"\t \\C [<captn>] -- set html3 caption (currently '%s')\n", ps->opt.caption? ps->opt.caption: "");
- fprintf(stderr,"\t \\c <dbname> -- connect to new database (currently '%s')\n", PQdb(ps->db));
+ fprintf(stderr,"\t \\connect <dbname> -- connect to new database (currently '%s')\n", PQdb(ps->db));
+ fprintf(stderr,"\t \\copy <dbname> -- copy table to/from a file\n");
fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>,* for all\n");
fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>,\\E execute too\n");
fprintf(stderr,"\t \\f [<sep>] -- change field separater (currently '%s')\n", ps->opt.fieldSep);
PGresult *
PSQLexec(PsqlSettings *ps, char *query)
{
- PGresult *res = PQexec(ps->db, query);
- if (!res)
- fputs(PQerrorMessage(ps->db), stderr);
- else
- {
- if (PQresultStatus(res)==PGRES_COMMAND_OK ||
- PQresultStatus(res)==PGRES_TUPLES_OK)
- return res;
- if (!ps->quiet)
- fputs(PQerrorMessage(ps->db), stderr);
- PQclear(res);
- }
- return NULL;
+ PGresult *res = PQexec(ps->db, query);
+ if (!res)
+ fputs(PQerrorMessage(ps->db), stderr);
+ else
+ {
+ if (PQresultStatus(res)==PGRES_COMMAND_OK ||
+ PQresultStatus(res)==PGRES_TUPLES_OK)
+ return res;
+ if (!ps->quiet)
+ fputs(PQerrorMessage(ps->db), stderr);
+ PQclear(res);
+ }
+ return NULL;
}
/*
* listAllDbs
{
PQprint(ps->queryFout,
results,
- &ps->opt);
+ &ps->opt);
PQclear(results);
return 0;
}
strcat(listbuf," and usesysid = relowner");
strcat(listbuf," ORDER BY relname ");
if (!(res=PSQLexec(ps, listbuf)))
- return -1;
+ return -1;
/* first, print out the attribute names */
nColumns = PQntuples(res);
if (nColumns > 0)
{
if ( deep_tablelist ) {
- /* describe everything here */
- char **table;
- table = (char**)malloc(nColumns * sizeof(char*));
- if ( table == NULL )
- perror("malloc");
-
- /* load table table */
- for (i=0; i < nColumns; i++) {
- table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1);
- if ( table[i] == NULL )
- perror("malloc");
- strcpy(table[i],PQgetvalue(res,i,1));
- }
-
- PQclear(res);
- for (i=0; i < nColumns; i++) {
- tableDesc(ps, table[i]);
- }
- free(table);
+ /* describe everything here */
+ char **table;
+ table = (char**)malloc(nColumns * sizeof(char*));
+ if ( table == NULL )
+ perror("malloc");
+
+ /* load table table */
+ for (i=0; i < nColumns; i++) {
+ table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1);
+ if ( table[i] == NULL )
+ perror("malloc");
+ strcpy(table[i],PQgetvalue(res,i,1));
+ }
+
+ PQclear(res);
+ for (i=0; i < nColumns; i++) {
+ tableDesc(ps, table[i]);
+ }
+ free(table);
}
else {
- /* Display the information */
-
- printf ("\nDatabase = %s\n", PQdb(ps->db));
- printf (" +------------------+----------------------------------+----------+\n");
- printf (" | Owner | Relation | Type |\n");
- printf (" +------------------+----------------------------------+----------+\n");
-
- /* next, print out the instances */
- for (i=0; i < PQntuples(res); i++) {
- printf (" | %-16.16s", PQgetvalue(res,i,0));
- printf (" | %-32.32s | ", PQgetvalue(res,i,1));
- rk = PQgetvalue(res,i,2);
- rr = PQgetvalue(res,i,3);
- if (strcmp(rk, "r") == 0)
- printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" );
- else
- printf ("%-8.8s |", "index");
- printf("\n");
- }
- printf (" +------------------+----------------------------------+----------+\n");
- PQclear(res);
+ /* Display the information */
+
+ printf ("\nDatabase = %s\n", PQdb(ps->db));
+ printf (" +------------------+----------------------------------+----------+\n");
+ printf (" | Owner | Relation | Type |\n");
+ printf (" +------------------+----------------------------------+----------+\n");
+
+ /* next, print out the instances */
+ for (i=0; i < PQntuples(res); i++) {
+ printf (" | %-16.16s", PQgetvalue(res,i,0));
+ printf (" | %-32.32s | ", PQgetvalue(res,i,1));
+ rk = PQgetvalue(res,i,2);
+ rr = PQgetvalue(res,i,3);
+ if (strcmp(rk, "r") == 0)
+ printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" );
+ else
+ printf ("%-8.8s |", "index");
+ printf("\n");
+ }
+ printf (" +------------------+----------------------------------+----------+\n");
+ PQclear(res);
}
return (0);
strcat(descbuf," and a.atttypid = t.oid ");
strcat(descbuf," ORDER BY attnum ");
if (!(res = PSQLexec(ps, descbuf)))
- return -1;
+ return -1;
/* first, print out the attribute names */
nColumns = PQntuples(res);
if (nColumns > 0)
printf ("%6i |", rsize > 0 ? rsize - 4 : 0 );
}
else {
- /* array types start with an underscore */
- if (rtype[0] != '_')
- printf ("%-32.32s |", rtype);
- else {
- char *newname;
- newname = malloc(strlen(rtype) + 2);
- strcpy(newname, rtype+1);
- strcat(newname, "[]");
- printf ("%-32.32s |", newname);
- free(newname);
- }
- if (rsize > 0)
- printf ("%6i |", rsize);
- else
- printf ("%6s |", "var");
+ /* array types start with an underscore */
+ if (rtype[0] != '_')
+ printf ("%-32.32s |", rtype);
+ else {
+ char *newname;
+ newname = malloc(strlen(rtype) + 2);
+ strcpy(newname, rtype+1);
+ strcat(newname, "[]");
+ printf ("%-32.32s |", newname);
+ free(newname);
+ }
+ if (rsize > 0)
+ printf ("%6i |", rsize);
+ else
+ printf ("%6s |", "var");
}
printf("\n");
}
len = strlen(line);
if (len == MAX_QUERY_BUFFER)
{
- fprintf(stderr, "line read exceeds maximum length. Truncating at %d\n", MAX_QUERY_BUFFER);
+ fprintf(stderr, "line read exceeds maximum length. Truncating at %d\n",
+ MAX_QUERY_BUFFER);
}
return line;
/*
* SendQuery: send the query string to the backend
- * return 0 if the query executed successfully
- * returns 1 otherwise
+ * return *success_p = 1 if the query executed successfully
+ * returns *success_p = 0 otherwise
*/
-int
-SendQuery(PsqlSettings *settings, char *query)
-{
+void
+SendQuery(bool *success_p, PsqlSettings *settings, const char *query,
+ const bool copy_in, const bool copy_out, FILE *copystream) {
+
PGresult *results;
PGnotify *notify;
- int status = 0;
if (settings->singleStep)
- fprintf(stdout, "\n*******************************************************************************\n");
+ fprintf(stdout, "\n**************************************"
+ "*****************************************\n");
if (settings->echoQuery || settings->singleStep) {
fprintf(stderr,"QUERY: %s\n",query);
}
if (settings->singleStep) {
- fprintf(stdout, "\n*******************************************************************************\n");
- fflush(stdout);
- printf("\npress return to continue ..\n");
- gets_fromFile("",stdin);
+ fprintf(stdout, "\n**************************************"
+ "*****************************************\n");
+ fflush(stdout);
+ printf("\npress return to continue ..\n");
+ gets_fromFile("",stdin);
}
results = PQexec(settings->db, query);
if (results == NULL) {
fprintf(stderr,"%s",PQerrorMessage(settings->db));
- return 1;
- }
-
- switch (PQresultStatus(results)) {
- case PGRES_TUPLES_OK:
- if (settings->gfname)
- {
- PsqlSettings ps=*settings;
- FILE *fp;
- ps.queryFout=stdout;
- fp=setFout(&ps, settings->gfname);
- if (!fp || fp==stdout)
- {
- status = 1;
- break;
- }
- PQprint(fp,
- results,
- &(settings->opt));
- if (ps.pipe)
- pclose(fp);
- else
- fclose(fp);
- settings->gfname=NULL;
- break;
- } else
- {
- PQprint(settings->queryFout,
- results,
- &(settings->opt));
- fflush(settings->queryFout);
- }
- PQclear(results);
- break;
- case PGRES_EMPTY_QUERY:
- /* do nothing */
- break;
- case PGRES_COMMAND_OK:
- if (!settings->quiet)
- fprintf(stdout,"%s\n", PQcmdStatus(results));
- break;
- case PGRES_COPY_OUT:
- handleCopyOut(results, settings->quiet);
- break;
- case PGRES_COPY_IN:
- handleCopyIn(results, settings->quiet);
- break;
- case PGRES_NONFATAL_ERROR:
- case PGRES_FATAL_ERROR:
- case PGRES_BAD_RESPONSE:
- status = 1;
- fprintf(stderr,"%s",PQerrorMessage(settings->db));
- break;
- }
-
- /* check for asynchronous returns */
- notify = PQnotifies(settings->db);
- if (notify) {
- fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
- notify->relname, notify->be_pid);
- free(notify);
+ *success_p = false;
+ } else {
+ switch (PQresultStatus(results)) {
+ case PGRES_TUPLES_OK:
+ if (settings->gfname) {
+ PsqlSettings ps=*settings;
+ FILE *fp;
+ ps.queryFout=stdout;
+ fp=setFout(&ps, settings->gfname);
+ if (!fp || fp==stdout) {
+ *success_p = false;
+ break;
+ } else *success_p = true;
+ PQprint(fp,
+ results,
+ &(settings->opt));
+ if (ps.pipe)
+ pclose(fp);
+ else
+ fclose(fp);
+ settings->gfname=NULL;
+ break;
+ } else {
+ *success_p = true;
+ PQprint(settings->queryFout,
+ results,
+ &(settings->opt));
+ fflush(settings->queryFout);
+ }
+ PQclear(results);
+ break;
+ case PGRES_EMPTY_QUERY:
+ *success_p = true;
+ break;
+ case PGRES_COMMAND_OK:
+ *success_p = true;
+ if (!settings->quiet)
+ fprintf(stdout,"%s\n", PQcmdStatus(results));
+ break;
+ case PGRES_COPY_OUT:
+ *success_p = true;
+ if (copy_out) {
+ handleCopyOut(results, settings->quiet, copystream);
+ } else {
+ if (!settings->quiet)
+ fprintf(stdout, "Copy command returns...\n");
+
+ handleCopyOut(results, settings->quiet, stdout);
+ }
+ break;
+ case PGRES_COPY_IN:
+ *success_p = true;
+ if (copy_in) {
+ handleCopyIn(results, false, copystream);
+ } else {
+ char c;
+ /*
+ * eat extra newline still in input buffer
+ *
+ */
+ fflush(stdin);
+ if ((c = getc(stdin)) != '\n' && c != EOF)
+ (void) ungetc(c, stdin);
+ handleCopyIn(results, !settings->quiet, stdin);
+ }
+ break;
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ case PGRES_BAD_RESPONSE:
+ *success_p = false;
+ fprintf(stderr,"%s",PQerrorMessage(settings->db));
+ break;
+ }
+
+ /* check for asynchronous returns */
+ notify = PQnotifies(settings->db);
+ if (notify) {
+ fprintf(stderr,
+ "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+ notify->relname, notify->be_pid);
+ free(notify);
+ }
}
+}
- return status;
-}
void
editFile(char *fname)
char *sys;
editorName = getenv("EDITOR");
if (!editorName)
- editorName = DEFAULT_EDITOR;
+ editorName = DEFAULT_EDITOR;
sys=malloc(strlen(editorName)+strlen(fname)+32+1);
if (!sys)
{
- perror("malloc");
- exit(1);
+ perror("malloc");
+ exit(1);
}
sprintf(sys, "exec '%s' '%s'", editorName, fname);
system(sys);
bool
toggle(PsqlSettings *settings, bool *sw, char *msg)
{
- *sw= !*sw;
- if (!settings->quiet)
- fprintf(stderr, "turned %s %s\n", on(*sw), msg);
- return *sw;
+ *sw= !*sw;
+ if (!settings->quiet)
+ fprintf(stderr, "turned %s %s\n", on(*sw), msg);
+ return *sw;
}
+
+
void
-decode(char *s)
-{
- char *p, *d;
- bool esc=0;
- for (d=p=s; *p; p++)
- {
- char c=*p;
- if (esc)
- {
- switch(*p)
- {
- case 'n':
- c='\n';
- break;
- case 'r':
- c='\r';
- break;
- case 't':
- c='\t';
- break;
- case 'f':
- c='\f';
- break;
- }
- esc=0;
- } else
- if (c=='\\')
- {
- esc=1;
- continue;
- }
- *d++=c;
- }
- *d='\0';
+unescape(char *dest, const char *source) {
+/*-----------------------------------------------------------------------------
+ Return as the string <dest> the value of string <source> with escape
+ sequences turned into the bytes they represent.
+-----------------------------------------------------------------------------*/
+ char *p;
+ bool esc; /* Last character we saw was the escape character (/) */
+
+ esc = false; /* Haven't seen escape character yet */
+ for (p = (char *)source; *p; p++) {
+ char c; /* Our output character */
+
+ if (esc) {
+ switch(*p) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case '\\':
+ c = '\\';
+ break;
+ default:
+ c = *p;
+ }
+ esc = false;
+ } else
+ if (*p == '\\') {
+ esc = true;
+ c = ' '; /* meaningless, but compiler doesn't know that */
+ } else {
+ c = *p;
+ esc = false;
+ }
+ if (!esc) *dest ++= c;
+ }
+ *dest = '\0'; /* Terminating null character */
}
+
+
+void
+parse_slash_copy(const char *args, char *table, const int table_len,
+ char *file, const int file_len,
+ bool *from_p, bool *error_p) {
+
+ char work_args[200];
+ /* A copy of the \copy command arguments, except that we modify it
+ as we parse to suit our parsing needs.
+ */
+ char *table_tok, *fromto_tok;
+
+ strncpy(work_args, args, sizeof(work_args));
+ work_args[sizeof(work_args)-1] = '\0';
+
+ *error_p = false; /* initial assumption */
+
+ table_tok = strtok(work_args, " ");
+ if (table_tok == NULL) {
+ fprintf(stderr, "\\copy needs arguments.\n");
+ *error_p = true;
+ } else {
+ strncpy(table, table_tok, table_len);
+ file[table_len-1] = '\0';
+
+ fromto_tok = strtok(NULL, " ");
+ if (fromto_tok == NULL) {
+ fprintf(stderr, "'FROM' or 'TO' must follow table name.\n");
+ *error_p = true;
+ } else {
+ if (strcasecmp(fromto_tok, "from") == 0) *from_p = true;
+ else if (strcasecmp(fromto_tok, "to") == 0) *from_p = false;
+ else {
+ fprintf(stderr,
+ "Unrecognized token found where "
+ "'FROM' or 'TO' expected: '%s'.\n",
+ fromto_tok);
+ *error_p = true;
+ }
+ if (!*error_p) {
+ char *file_tok;
+
+ file_tok = strtok(NULL, " ");
+ if (file_tok == NULL) {
+ fprintf(stderr, "A file pathname must follow '%s'.\n",
+ fromto_tok);
+ *error_p = true;
+ } else {
+ strncpy(file, file_tok, file_len);
+ file[file_len-1] = '\0';
+ if (strtok(NULL, " ") != NULL) {
+ fprintf(stderr,
+ "You have extra tokens after the filename.\n");
+ *error_p = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+void
+do_copy(const char *args, PsqlSettings *settings) {
+/*---------------------------------------------------------------------------
+ Execute a \copy command (frontend copy). We have to open a file, then
+ submit a COPY query to the backend and either feed it data from the
+ file or route its response into the file.
+
+ We do a text copy with default (tab) column delimiters. Some day, we
+ should do all the things a backend copy can do.
+
+----------------------------------------------------------------------------*/
+ char query[200];
+ /* The COPY command we send to the back end */
+ bool from;
+ /* The direction of the copy is from a file to a table. */
+ char file[MAXPATHLEN+1];
+ /* The pathname of the file from/to which we copy */
+ char table[NAMEDATALEN+1];
+ /* The name of the table from/to which we copy */
+ bool syntax_error;
+ /* The \c command has invalid syntax */
+ FILE *copystream;
+
+ parse_slash_copy(args, table, sizeof(table), file, sizeof(file),
+ &from, &syntax_error);
+
+ if (!syntax_error) {
+ strcpy(query, "COPY ");
+ strcat(query, table);
+
+ if (from)
+ strcat(query, " FROM stdin");
+ else
+ strcat(query, " TO stdout");
+
+ if (from) {
+ copystream = fopen(file, "r");
+ } else {
+ copystream = fopen(file, "w");
+ }
+ if (copystream < 0)
+ fprintf(stderr,
+ "Unable to open file %s which to copy, errno = %s (%d).",
+ from ? "from" : "to", strerror(errno), errno);
+ else {
+ bool success; /* The query succeeded at the backend */
+
+ SendQuery(&success, settings, query, from, !from, copystream);
+ fclose(copystream);
+ if (!settings->quiet) {
+ if (success)
+ fprintf(stdout, "Successfully copied.\n");
+ else
+ fprintf(stdout, "Copy failed.\n");
+ }
+ }
+ }
+}
+
+
+void
+do_connect(const char *new_dbname, PsqlSettings *settings) {
+
+ char *dbname=PQdb(settings->db);
+ if (!new_dbname)
+ fprintf(stderr,"\\connect must be followed by a database name\n");
+ else {
+ PGconn *olddb=settings->db;
+
+ printf("closing connection to database: %s\n", dbname);
+ settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
+ NULL, NULL, new_dbname);
+ printf("connecting to new database: %s\n", new_dbname);
+ if (PQstatus(settings->db) == CONNECTION_BAD) {
+ fprintf(stderr,"%s\n", PQerrorMessage(settings->db));
+ printf("reconnecting to %s\n", dbname);
+ settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
+ NULL, NULL, dbname);
+ if (PQstatus(settings->db) == CONNECTION_BAD) {
+ fprintf(stderr,
+ "could not reconnect to %s. exiting\n", dbname);
+ exit(2);
+ }
+ } else {
+ PQfinish(olddb);
+ free(settings->prompt);
+ settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
+ sprintf(settings->prompt,"%s=> ", PQdb(settings->db));
+ }
+ }
+}
+
+
+void
+do_edit(const char *filename_arg, char *query, int *retcode_p) {
+
+ int fd;
+ char tmp[64];
+ char *fname;
+ int cc;
+ const int ql = strlen(query);
+ bool error;
+
+ if (filename_arg) {
+ fname=(char *)filename_arg;
+ error=false;
+ } else {
+ sprintf(tmp, "/tmp/psql.%d.%d", geteuid(), getpid());
+ fname=tmp;
+ unlink(tmp);
+ if (ql > 0) {
+ if ((fd=open(tmp, O_EXCL|O_CREAT|O_WRONLY, 0600)) == -1) {
+ perror(tmp);
+ error=true;
+ } else {
+ if (query[ql-1] != '\n')
+ strcat(query, "\n");
+ if (write(fd, query, ql) != ql) {
+ perror(tmp);
+ close(fd);
+ unlink(tmp);
+ error=true;
+ } else error=false;
+ close(fd);
+ }
+ } else error=false;
+ }
+
+ if (error) *retcode_p=1;
+ else {
+ editFile(fname);
+ if ((fd=open(fname, O_RDONLY))==-1) {
+ perror(fname);
+ if (!filename_arg)
+ unlink(fname);
+ *retcode_p=1;
+ } else {
+ if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1) {
+ perror(fname);
+ close(fd);
+ if (!filename_arg)
+ unlink(fname);
+ *retcode_p=1;
+ } else {
+ query[cc]='\0';
+ close(fd);
+ if (!filename_arg)
+ unlink(fname);
+ rightTrim(query);
+ if (query[strlen(query)-1]==';') *retcode_p=0;
+ else *retcode_p=1;
+ }
+ }
+ }
+}
+
+
+
+void
+do_help(const char *topic) {
+
+ if (!topic) {
+ char left_center_right; /* Which column we're displaying */
+ int i; /* Index into QL_HELP[] */
+
+ printf("type \\h <cmd> where <cmd> is one of the following:\n");
+
+ left_center_right = 'L'; /* Start with left column */
+ i = 0;
+ while (QL_HELP[i].cmd != NULL) {
+ switch(left_center_right) {
+ case 'L':
+ printf(" %-25s", QL_HELP[i].cmd);
+ left_center_right = 'C';
+ break;
+ case 'C':
+ printf("%-25s", QL_HELP[i].cmd);
+ left_center_right = 'R';
+ break;
+ case 'R':
+ printf("%-25s\n", QL_HELP[i].cmd);
+ left_center_right = 'L';
+ break;
+ };
+ i++;
+ }
+ if (left_center_right != 'L') puts("\n");
+ printf("type \\h * for a complete description of all commands\n");
+ } else {
+ int i; /* Index into QL_HELP[] */
+ bool help_found; /* We found the help he asked for */
+
+ help_found = false; /* Haven't found it yet */
+ for (i=0; QL_HELP[i].cmd; i++) {
+ if (strcmp(QL_HELP[i].cmd, topic) == 0 ||
+ strcmp(topic, "*") == 0 ) {
+ help_found = true;
+ printf("Command: %s\n",QL_HELP[i].cmd);
+ printf("Description: %s\n", QL_HELP[i].help);
+ printf("Syntax:\n");
+ printf("%s\n", QL_HELP[i].syntax);
+ printf("\n");
+ }
+ }
+ if (!help_found)
+ printf("command not found, "
+ "try \\h with no arguments to see available help\n");
+ }
+}
+
+
+
+void
+do_shell(const char *command) {
+
+ if (!command) {
+ char *sys;
+ char *shellName;
+
+ shellName = getenv("SHELL");
+ if (shellName == NULL)
+ shellName = DEFAULT_SHELL;
+ sys = malloc(strlen(shellName)+16);
+ if (!sys) {
+ perror("malloc");
+ exit(1);
+ }
+ sprintf(sys,"exec %s", shellName);
+ system(sys);
+ free(sys);
+ } else system(command);
+}
+
+
+
/*
HandleSlashCmds:
line is the current input line
prompt_ptr is a pointer to the prompt string,
a pointer is used because the prompt can be used with
- a connection to a new database
+ a connection to a new database
returns a status:
0 - send currently constructed query to backend (i.e. we got a \g)
1 - skip processing of this line, continue building up query
*/
int
HandleSlashCmds(PsqlSettings *settings,
- char *line,
- char *query)
+ char *line,
+ char *query)
{
int status = 1;
- char *optarg = NULL;
- int len;
+ char *optarg;
+ /* Pointer inside the <cmd> string to the argument of the slash
+ command, assuming it is a one-character slash command. If it's
+ not a one-character command, this is meaningless.
+ */
+ char *optarg2;
+ /* Pointer inside the <cmd> string to the argument of the slash
+ command assuming it's not a one-character command. If it's a
+ one-character command, this is meaningless.
+ */
+ char *cmd;
+ /* String: value of the slash command, less the slash and with escape
+ sequences decoded.
+ */
+ int blank_loc;
+ /* Offset within <cmd> of first blank */
+
+ cmd = malloc(strlen(line)); /* unescaping better not make string grow. */
+
+ unescape(cmd, line+1); /* sets cmd string */
+
+ /* Originally, there were just single character commands. Now,
+ we define some longer, friendly commands, but we have to keep
+ the old single character commands too. \c used to be what
+ \connect is now. Complicating matters is the fact that with
+ the single-character commands, you can start the argument
+ right after the single character, so "\copy" would mean
+ "connect to database named 'opy'".
+ */
- len = strlen(line);
- if (len > 2)
- {
- optarg = leftTrim(line+2);
- decode(optarg);
- }
- switch (line[1])
+ if (strlen(cmd) > 1) optarg = cmd+1 + strspn(cmd+1, " \t");
+ else optarg = NULL;
+
+ blank_loc = strcspn(cmd, " \t");
+ if (blank_loc == 0) optarg2 = NULL;
+ else optarg2 = cmd + blank_loc + strspn(cmd+blank_loc, " \t");
+
+
+ switch (cmd[0])
{
case 'a': /* toggles to align fields on output */
toggle(settings, &settings->opt.align, "field alignment");
- break;
+ break;
case 'C': /* define new caption */
- if (settings->opt.caption)
- free(settings->opt.caption);
- if (!optarg)
- settings->opt.caption=NULL;
- else
- if (!(settings->opt.caption=strdup(optarg)))
- {
- perror("malloc");
- exit(1);
- }
- break;
- case 'c': /* \c means connect to new database */
- {
- char *dbname=PQdb(settings->db);
- if (!optarg) {
- fprintf(stderr,"\\c must be followed by a database name\n");
- break;
- }
- {
- PGconn *olddb=settings->db;
-
- printf("closing connection to database: %s\n", dbname);
- settings->db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg);
- printf("connecting to new database: %s\n", optarg);
- if (PQstatus(settings->db) == CONNECTION_BAD) {
- fprintf(stderr,"%s\n", PQerrorMessage(settings->db));
- printf("reconnecting to %s\n", dbname);
- settings->db = PQsetdb(PQhost(olddb), PQport(olddb),
- NULL, NULL, dbname);
- if (PQstatus(settings->db) == CONNECTION_BAD) {
- fprintf(stderr,
- "could not reconnect to %s. exiting\n", dbname);
- exit(2);
- }
- break;
- }
- PQfinish(olddb);
- free(settings->prompt);
- settings->prompt = malloc(strlen(PQdb(settings->db)) + 10);
- sprintf(settings->prompt,"%s=> ", PQdb(settings->db));
- break;
- }
+ if (settings->opt.caption)
+ free(settings->opt.caption);
+ if (!optarg)
+ settings->opt.caption=NULL;
+ else
+ if (!(settings->opt.caption=strdup(optarg)))
+ {
+ perror("malloc");
+ exit(1);
+ }
+ break;
+ case 'c': {
+ if (strncmp(cmd, "copy ", strlen("copy ")) == 0)
+ do_copy(optarg2, settings);
+ else if (strncmp(cmd, "connect ", strlen("connect ")) == 0)
+ do_connect(optarg2, settings);
+ else
+ do_connect(optarg, settings);
}
break;
case 'd': /* \d describe tables or columns in a table */
- if (!optarg) {
+ if (!optarg) {
tableList(settings, 0);
- break;
- }
- if (strcmp(optarg, "*") == 0 ) {
- tableList(settings, 0);
- tableList(settings, 1);
- }
- else {
- tableDesc(settings, optarg);
- }
- break;
- case 'e':
+ break;
+ }
+ if (strcmp(optarg, "*") == 0 ) {
+ tableList(settings, 0);
+ tableList(settings, 1);
+ }
+ else {
+ tableDesc(settings, optarg);
+ }
+ break;
+ case 'e': /* edit */
{
- int fd;
- char tmp[64];
- char *fname;
- int cc;
- int ql = strlen(query);
- if (optarg)
- fname=optarg;
- else
- {
- sprintf(tmp, "/tmp/psql.%d.%d", geteuid(), getpid());
- fname=tmp;
- unlink(tmp);
- if (ql)
- {
- if ((fd=open(tmp, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1)
- {
- perror(tmp);
- break;
- }
- if (query[ql-1]!='\n')
- strcat(query, "\n");
- if (write(fd, query, ql)!=ql)
- {
- perror(tmp);
- close(fd);
- unlink(tmp);
- break;
- }
- close(fd);
- }
- }
- editFile(fname);
- if ((fd=open(fname, O_RDONLY))==-1)
- {
- perror(fname);
- if (!optarg)
- unlink(fname);
- break;
- }
- if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1)
- {
- perror(fname);
- close(fd);
- if (!optarg)
- unlink(fname);
- break;
- }
- query[cc]='\0';
- close(fd);
- if (!optarg)
- unlink(fname);
- rightTrim(query);
- if (query[strlen(query)-1]==';')
- return 0;
- break;
+ do_edit(optarg, query, &status);
+ break;
}
case 'E':
{
- FILE *fd;
- static char *lastfile;
- struct stat st, st2;
- if (optarg)
- {
- if (lastfile)
- free(lastfile);
- lastfile=malloc(strlen(optarg+1));
- if (!lastfile)
- {
- perror("malloc");
- exit(1);
- }
- strcpy(lastfile, optarg);
- } else if (!lastfile)
- {
- fprintf(stderr,"\\r must be followed by a file name initially\n");
- break;
- }
- stat(lastfile, &st);
- editFile(lastfile);
- if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL))
- {
- perror(lastfile);
- break;
- }
- if (st2.st_mtime==st.st_mtime)
- {
- if (!settings->quiet)
- fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
- fclose(fd);
- break;
- }
- MainLoop(settings, fd);
- fclose(fd);
- break;
+ FILE *fd;
+ static char *lastfile;
+ struct stat st, st2;
+ if (optarg)
+ {
+ if (lastfile)
+ free(lastfile);
+ lastfile=malloc(strlen(optarg+1));
+ if (!lastfile)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ strcpy(lastfile, optarg);
+ } else if (!lastfile)
+ {
+ fprintf(stderr,"\\r must be followed by a file name initially\n");
+ break;
+ }
+ stat(lastfile, &st);
+ editFile(lastfile);
+ if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL))
+ {
+ perror(lastfile);
+ break;
+ }
+ if (st2.st_mtime==st.st_mtime)
+ {
+ if (!settings->quiet)
+ fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
+ fclose(fd);
+ break;
+ }
+ MainLoop(settings, fd);
+ fclose(fd);
+ break;
}
case 'f':
{
char *fs=DEFAULT_FIELD_SEP;
- if (optarg)
- fs=optarg;
+ if (optarg)
+ fs=optarg;
if (settings->opt.fieldSep);
- free(settings->opt.fieldSep);
- if (!(settings->opt.fieldSep=strdup(fs)))
- {
- perror("malloc");
- exit(1);
- }
- if (!settings->quiet)
- fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
- break;
+ free(settings->opt.fieldSep);
+ if (!(settings->opt.fieldSep=strdup(fs)))
+ {
+ perror("malloc");
+ exit(1);
+ }
+ if (!settings->quiet)
+ fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
+ break;
}
case 'g': /* \g means send query */
settings->gfname = optarg;
status = 0;
break;
- case 'h':
+ case 'h': /* help */
{
- char *cmd;
- int i, numCmds;
- int all_help = 0;
- char left_center_right = 'L';
-
- if (!optarg) {
- printf("type \\h <cmd> where <cmd> is one of the following:\n");
- i = 0;
- while (QL_HELP[i].cmd != NULL)
- {
- switch(left_center_right)
- {
- case 'L':
- printf(" %-25s", QL_HELP[i].cmd);
- left_center_right = 'C';
- break;
- case 'C':
- printf("%-25s", QL_HELP[i].cmd);
- left_center_right = 'R';
- break;
- case 'R':
- printf("%-25s\n", QL_HELP[i].cmd);
- left_center_right = 'L';
- break;
- };
- i++;
- }
- if (left_center_right != 'L')
- puts("\n");
- printf("type \\h * for a complete description of all commands\n");
- }
- else
- {
- cmd = optarg;
-
- numCmds = 0;
- while (QL_HELP[numCmds++].cmd != NULL);
-
- numCmds = numCmds - 1;
-
- if (strcmp(cmd, "*") == 0 ) {
- all_help=1;
- }
-
- for (i=0; i<numCmds;i++) {
- if (strcmp(QL_HELP[i].cmd, cmd) == 0 || all_help) {
- printf("Command: %s\n",QL_HELP[i].cmd);
- printf("Description: %s\n", QL_HELP[i].help);
- printf("Syntax:\n");
- printf("%s\n", QL_HELP[i].syntax);
- if ( all_help ) {
- printf("\n");
- }
- else {
- break;
- }
- }
- }
- if (i == numCmds && ! all_help)
- printf("command not found, try \\h with no arguments to see available help\n");
- }
- break;
+ do_help(optarg);
+ break;
}
case 'i': /* \i is include file */
{
- FILE *fd;
-
- if (!optarg) {
- fprintf(stderr,"\\i must be followed by a file name\n");
- break;
- }
-
- if ((fd = fopen(optarg, "r")) == NULL)
- {
- fprintf(stderr,"file named %s could not be opened\n",optarg);
- break;
- }
- MainLoop(settings, fd);
- fclose(fd);
- break;
+ FILE *fd;
+
+ if (!optarg) {
+ fprintf(stderr,"\\i must be followed by a file name\n");
+ break;
+ }
+
+ if ((fd = fopen(optarg, "r")) == NULL)
+ {
+ fprintf(stderr,"file named %s could not be opened\n",optarg);
+ break;
+ }
+ MainLoop(settings, fd);
+ fclose(fd);
+ break;
}
case 'l': /* \l is list database */
listAllDbs(settings);
break;
case 'H':
- if (toggle(settings, &settings->opt.html3, "HTML3.0 tablular output"))
+ if (toggle(settings, &settings->opt.html3, "HTML3.0 tabular output"))
settings->opt.standard = 0;
break;
case 'o':
case 'p':
if (query)
{
- fputs(query, stdout);
- fputc('\n', stdout);
+ fputs(query, stdout);
+ fputc('\n', stdout);
}
break;
case 'q': /* \q is quit */
case 'r': /* reset(clear) the buffer */
query[0]='\0';
if (!settings->quiet)
- fprintf(stderr, "buffer reset(cleared)\n");
+ fprintf(stderr, "buffer reset(cleared)\n");
break;
case 's': /* \s is save history to a file */
- if (!optarg)
- optarg="/dev/tty";
- if (write_history(optarg) != 0)
- fprintf(stderr,"cannot write history to %s\n",optarg);
- break;
+ if (!optarg)
+ optarg="/dev/tty";
+ if (write_history(optarg) != 0)
+ fprintf(stderr,"cannot write history to %s\n",optarg);
+ break;
case 'm': /* monitor like type-setting */
if (toggle(settings, &settings->opt.standard, "standard SQL separaters and padding"))
{
settings->opt.html3 = settings->opt.expanded = 0;
settings->opt.align = settings->opt.header = 1;
- free(settings->opt.fieldSep);
- settings->opt.fieldSep=strdup("|");
- if (!settings->quiet)
- fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
+ free(settings->opt.fieldSep);
+ settings->opt.fieldSep=strdup("|");
+ if (!settings->quiet)
+ fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
} else
{
- free(settings->opt.fieldSep);
- settings->opt.fieldSep=strdup(DEFAULT_FIELD_SEP);
- if (!settings->quiet)
- fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
+ free(settings->opt.fieldSep);
+ settings->opt.fieldSep=strdup(DEFAULT_FIELD_SEP);
+ if (!settings->quiet)
+ fprintf(stderr, "field separater changed to '%s'\n", settings->opt.fieldSep);
}
break;
case 't': /* toggle headers */
toggle(settings, &settings->opt.header, "output headings and row count");
break;
case 'T': /* define html <table ...> option */
- if (settings->opt.tableOpt)
- free(settings->opt.tableOpt);
- if (!optarg)
- settings->opt.tableOpt=NULL;
- else
- if (!(settings->opt.tableOpt=strdup(optarg)))
- {
- perror("malloc");
- exit(1);
- }
- break;
+ if (settings->opt.tableOpt)
+ free(settings->opt.tableOpt);
+ if (!optarg)
+ settings->opt.tableOpt=NULL;
+ else
+ if (!(settings->opt.tableOpt=strdup(optarg)))
+ {
+ perror("malloc");
+ exit(1);
+ }
+ break;
case 'x':
toggle(settings, &settings->opt.expanded, "expanded table representation");
break;
case '!':
- if (!optarg) {
- char *sys;
- char *shellName;
- shellName = getenv("SHELL");
- if (shellName == NULL)
- shellName = DEFAULT_SHELL;
- sys = malloc(strlen(shellName)+16);
- if (!sys)
- {
- perror("malloc");
- exit(1);
- }
- sprintf(sys,"exec %s", shellName);
- system(sys);
- free(sys);
- }
- else
- system(optarg);
+ do_shell(optarg);
break;
default:
case '?': /* \? is help */
slashUsage(settings);
break;
}
+ free(cmd);
return status;
}
int
MainLoop(PsqlSettings *settings, FILE *source)
{
- char *line; /* line of input */
- int len; /* length of the line */
- char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
- int exitStatus = 0;
- int slashCmdStatus = 0;
- /* slashCmdStatus can be:
+ char *line; /* line of input */
+ int len; /* length of the line */
+ char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
+ int exitStatus = 0;
+ int slashCmdStatus = 0;
+ /* slashCmdStatus can be:
0 - send currently constructed query to backend (i.e. we got a \g)
1 - skip processing of this line, continue building up query
2 - terminate processing of this query entirely
- */
-
- bool sendQuery = 0;
- bool querySent = 0;
- bool interactive;
- READ_ROUTINE GetNextLine;
- bool connected = 1;
- /* We are connected to the backend (last time we looked) */
- bool eof = 0;
- /* We've reached the end of our command input. */
-
- interactive = ((source == stdin) && !settings->notty);
+ */
+
+ bool sendQuery = 0;
+ bool querySent = 0;
+ bool interactive;
+ READ_ROUTINE GetNextLine;
+ bool connected = 1;
+ /* We are connected to the backend (last time we looked) */
+ bool eof = 0;
+ /* We've reached the end of our command input. */
+
+ interactive = ((source == stdin) && !settings->notty);
#define PROMPT "=> "
- if (interactive) {
- if (settings->prompt)
- free(settings->prompt);
- settings->prompt = malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1);
- if (settings->quiet)
- settings->prompt[0] = '\0';
- else
- sprintf(settings->prompt,"%s%s", PQdb(settings->db), PROMPT);
- if (settings->useReadline) {
- using_history();
- GetNextLine = gets_readline;
- } else
- GetNextLine = gets_noreadline;
-
- }
- else
- GetNextLine = gets_fromFile;
-
- query[0] = '\0';
+ if (interactive) {
+ if (settings->prompt)
+ free(settings->prompt);
+ settings->prompt =
+ malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1);
+ if (settings->quiet)
+ settings->prompt[0] = '\0';
+ else
+ sprintf(settings->prompt,"%s%s", PQdb(settings->db), PROMPT);
+ if (settings->useReadline) {
+ using_history();
+ GetNextLine = gets_readline;
+ } else GetNextLine = gets_noreadline;
+ } else GetNextLine = gets_fromFile;
+
+ query[0] = '\0';
- /* main loop for getting queries and executing them */
- while (connected && !eof) {
- line = GetNextLine(settings->prompt, source);
- if (line == NULL) { /* No more input. Time to quit */
- printf("EOF\n"); /* Goes on prompt line */
- eof = 1;
- } else {
- exitStatus = 0;
- line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */
-
- if (line[0] == '\0') {
- free(line);
- continue;
- }
-
- /* filter out comment lines that begin with --,
- this could be incorrect if -- is part of a quoted string.
- But we won't go through the trouble of detecting that. If you have
- -- in your quoted string, be careful and don't start a line with it */
- if (line[0] == '-' && line[1] == '-') {
- if (settings->singleStep) /* in single step mode, show comments */
- fprintf(stdout,"%s\n",line);
- free(line);
- continue;
- }
- if (line[0] != '\\' && querySent)
- {
- query[0]='\0';
- querySent = 0;
- }
-
- len = strlen(line);
-
- if (interactive && settings->useReadline)
- add_history(line); /* save non-empty lines in history */
+ /* main loop for getting queries and executing them */
+ while (connected && !eof) {
+ line = GetNextLine(settings->prompt, source);
+ if (line == NULL) { /* No more input. Time to quit */
+ printf("EOF\n"); /* Goes on prompt line */
+ eof = true;
+ } else {
+ exitStatus = 0;
+ line = rightTrim(line);
+ /* remove whitespaces on the right, incl. \n's */
+
+ if (line[0] == '\0') {
+ free(line);
+ continue;
+ }
+
+ /* filter out comment lines that begin with --,
+ this could be incorrect if -- is part of a quoted string.
+ But we won't go through the trouble of detecting that.
+ If you have -- in your quoted string, be careful and don't
+ start a line with it
+ */
+ if (line[0] == '-' && line[1] == '-') {
+ if (settings->singleStep)
+ /* in single step mode, show comments */
+ fprintf(stdout,"%s\n",line);
+ free(line);
+ continue;
+ }
+ if (line[0] != '\\' && querySent) {
+ query[0]='\0';
+ querySent = 0;
+ }
+
+ len = strlen(line);
+
+ if (interactive && settings->useReadline)
+ add_history(line); /* save non-empty lines in history */
- /* do the query immediately if we are doing single line queries
- or if the last character is a semicolon */
- sendQuery = settings->singleLineMode || (line[len-1] == ';') ;
-
- /* normally, \ commands have to be start the line,
- but for backwards compatibility with monitor,
- check for \g at the end of line */
- if (len > 2 && !sendQuery)
- {
- if (line[len-1]=='g' && line[len-2]=='\\')
- {
- sendQuery = 1;
- line[len-2]='\0';
- }
- }
+ /* do the query immediately if we are doing single line queries
+ or if the last character is a semicolon
+ */
+ sendQuery = settings->singleLineMode || (line[len-1] == ';') ;
+
+ /* normally, \ commands have to be start the line,
+ but for backwards compatibility with monitor,
+ check for \g at the end of line */
+ if (len > 2 && !sendQuery) {
+ if (line[len-1]=='g' && line[len-2]=='\\') {
+ sendQuery = 1;
+ line[len-2]='\0';
+ }
+ }
- /* slash commands have to be on their own line */
- if (line[0] == '\\') {
- slashCmdStatus = HandleSlashCmds(settings,
- line,
- query);
- if (slashCmdStatus == 1) {
- free(line);
- continue;
- }
- if (slashCmdStatus == 2) {
- free(line);
- break;
- }
- if (slashCmdStatus == 0)
- sendQuery = 1;
- }
- else
- if (strlen(query) + len > MAX_QUERY_BUFFER)
- {
- fprintf(stderr,"query buffer max length of %d exceeded\n",MAX_QUERY_BUFFER);
- fprintf(stderr,"query line ignored\n");
- }
- else
- if (query[0]!='\0') {
- strcat(query,"\n");
- strcat(query,line);
- }
- else
- strcpy(query,line);
-
- if (sendQuery && query[0] != '\0')
- {
- /* echo the line read from the file,
- unless we are in single_step mode, because single_step mode
- will echo anyway */
- if (!interactive && !settings->singleStep && !settings->quiet)
- fprintf(stderr,"%s\n", query);
-
- exitStatus = SendQuery(settings, query);
- querySent = 1;
- if (PQstatus(settings->db) == CONNECTION_BAD) {
- connected = 0;
- fprintf(stderr, "We have lost the connection to the backend, so "
- "further processing is impossible. Terminating.\n");
- }
- }
- free(line); /* free storage malloc'd by GetNextLine */
- }
- } /* while */
- return exitStatus;
+ /* slash commands have to be on their own line */
+ if (line[0] == '\\') {
+ slashCmdStatus = HandleSlashCmds(settings,
+ line,
+ query);
+ if (slashCmdStatus == 1) {
+ free(line);
+ continue;
+ }
+ if (slashCmdStatus == 2) {
+ free(line);
+ break;
+ }
+ if (slashCmdStatus == 0)
+ sendQuery = 1;
+ } else if (strlen(query) + len > MAX_QUERY_BUFFER) {
+ fprintf(stderr,"query buffer max length of %d exceeded\n",
+ MAX_QUERY_BUFFER);
+ fprintf(stderr,"query line ignored\n");
+ } else if (query[0]!='\0') {
+ strcat(query,"\n");
+ strcat(query,line);
+ } else strcpy(query,line);
+ if (sendQuery && query[0] != '\0') {
+ /* echo the line read from the file,
+ unless we are in single_step mode, because single_step mode
+ will echo anyway
+ */
+ bool success; /* The query succeeded at the backend */
+
+ if (!interactive && !settings->singleStep && !settings->quiet)
+ fprintf(stderr,"%s\n", query);
+
+ SendQuery(&success, settings, query, false, false, 0);
+ exitStatus = success ? 0 : 1;
+ querySent = 1;
+ if (PQstatus(settings->db) == CONNECTION_BAD) {
+ connected = 0;
+ fprintf(stderr,
+ "We have lost the connection to the backend, so "
+ "further processing is impossible. "
+ "Terminating.\n");
+ }
+ }
+ free(line); /* free storage malloc'd by GetNextLine */
+ }
+ } /* while */
+ return exitStatus;
}
+
+
int
main(int argc, char **argv)
{
settings.opt.fieldSep=strdup(DEFAULT_FIELD_SEP);
settings.opt.pager = 1;
if (!isatty(0) || !isatty(1))
- settings.quiet = settings.notty = 1;
+ settings.quiet = settings.notty = 1;
#ifndef NOREADLINE
else
- settings.useReadline = 1;
+ settings.useReadline = 1;
#endif
while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lh:Hnso:p:qStT:x")) != EOF) {
switch (c) {
case 'A':
- settings.opt.align = 0;
- break;
+ settings.opt.align = 0;
+ break;
case 'a':
- fe_setauthsvc(optarg, errbuf);
- break;
+ fe_setauthsvc(optarg, errbuf);
+ break;
case 'c':
- singleQuery = optarg;
- if ( singleQuery[0] == '\\' ) {
- singleSlashCmd=1;
- }
- break;
+ singleQuery = optarg;
+ if ( singleQuery[0] == '\\' ) {
+ singleSlashCmd=1;
+ }
+ break;
case 'd':
dbname = optarg;
break;
dbname = PQdb(settings.db);
if (PQstatus(settings.db) == CONNECTION_BAD) {
- fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
- fprintf(stderr,"%s",PQerrorMessage(settings.db));
- exit(1);
+ fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
+ fprintf(stderr,"%s",PQerrorMessage(settings.db));
+ exit(1);
}
if (listDatabases) {
exit(listAllDbs(&settings));
- }
+ }
if (!settings.quiet && !singleQuery && !qfilename) {
- printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
- printf(" Please read the file COPYRIGHT for copyright terms of POSTGRES95\n\n");
- printf(" type \\? for help on slash commands\n");
- printf(" type \\q to quit\n");
- printf(" type \\g or terminate with semicolon to execute query\n");
- printf(" You are currently connected to the database: %s\n\n", dbname);
- }
-
+ printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
+ printf(" Please read the file COPYRIGHT for copyright terms "
+ "of POSTGRES95\n\n");
+ printf(" type \\? for help on slash commands\n");
+ printf(" type \\q to quit\n");
+ printf(" type \\g or terminate with semicolon to execute query\n");
+ printf(" You are currently connected to the database: %s\n\n", dbname);
+ }
+
if (qfilename || singleSlashCmd) {
/* read in a file full of queries instead of reading in queries
- interactively */
+ interactively */
char *line;
if ( singleSlashCmd ) {
- /* Not really a query, but "Do what I mean, not what I say." */
- line = singleQuery;
- }
- else {
- line = malloc(strlen(qfilename) + 5);
- sprintf(line,"\\i %s", qfilename);
+ /* Not really a query, but "Do what I mean, not what I say." */
+ line = singleQuery;
+ } else {
+ line = malloc(strlen(qfilename) + 5);
+ sprintf(line,"\\i %s", qfilename);
}
HandleSlashCmds(&settings, line, "");
-
- } else {
- if (singleQuery) {
- exitStatus = SendQuery(&settings, singleQuery);
- }
- else
- exitStatus = MainLoop(&settings, stdin);
- }
+ } else {
+ if (singleQuery) {
+ bool success; /* The query succeeded at the backend */
+ SendQuery(&success, &settings, singleQuery, false, false, 0);
+ exitStatus = success ? 0 : 1;
+ } else exitStatus = MainLoop(&settings, stdin);
+ }
PQfinish(settings.db);
return exitStatus;
}
-#define COPYBUFSIZ 8192
+#define COPYBUFSIZ 8192
static void
-handleCopyOut(PGresult *res, bool quiet)
-{
- bool copydone = false;
+handleCopyOut(PGresult *res, bool quiet, FILE *copystream) {
+ bool copydone;
char copybuf[COPYBUFSIZ];
int ret;
- if (!quiet)
- fprintf(stdout, "Copy command returns...\n");
-
+ copydone = false; /* Can't be done; haven't started. */
+
while (!copydone) {
- ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
-
- if (copybuf[0] == '\\' &&
- copybuf[1] == '.' &&
- copybuf[2] =='\0') {
- copydone = true; /* don't print this... */
- } else {
- fputs(copybuf, stdout);
- switch (ret) {
- case EOF:
- copydone = true;
- /*FALLTHROUGH*/
- case 0:
- fputc('\n', stdout);
- break;
- case 1:
- break;
- }
- }
+ ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
+
+ if (copybuf[0] == '\\' &&
+ copybuf[1] == '.' &&
+ copybuf[2] =='\0') {
+ copydone = true; /* don't print this... */
+ } else {
+ fputs(copybuf, copystream);
+ switch (ret) {
+ case EOF:
+ copydone = true;
+ /*FALLTHROUGH*/
+ case 0:
+ fputc('\n', copystream);
+ break;
+ case 1:
+ break;
+ }
+ }
}
- fflush(stdout);
+ fflush(copystream);
PQendcopy(res->conn);
}
+
static void
-handleCopyIn(PGresult *res, bool quiet)
-{
+handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) {
bool copydone = false;
bool firstload;
bool linedone;
char *s;
int buflen;
int c;
-
- if (!quiet) {
- fputs("Enter info followed by a newline\n", stdout);
- fputs("End with a backslash and a period on a line by itself.\n", stdout);
+
+ if (mustprompt) {
+ fputs("Enter info followed by a newline\n", stdout);
+ fputs("End with a backslash and a "
+ "period on a line by itself.\n", stdout);
}
- /*
- * eat extra newline still in input buffer
- *
- */
- fflush(stdin);
- if ((c = getc(stdin)) != '\n' && c != EOF) {
- (void) ungetc(c, stdin);
+ while (!copydone) { /* for each input line ... */
+ if (mustprompt) {
+ fputs(">> ", stdout);
+ fflush(stdout);
}
-
- while (!copydone) { /* for each input line ... */
- if (!quiet) {
- fputs(">> ", stdout);
- fflush(stdout);
- }
- firstload = true;
- linedone = false;
- while (!linedone) { /* for each buffer ... */
- s = copybuf;
- buflen = COPYBUFSIZ;
- for (; buflen > 1 &&
- !(linedone = (c = getc(stdin)) == '\n' || c == EOF);
- --buflen) {
- *s++ = c;
- }
- if (c == EOF) {
- /* reading from stdin, but from a file */
- PQputline(res->conn, "\\.");
- copydone = true;
- break;
- }
- *s = '\0';
- PQputline(res->conn, copybuf);
- if (firstload) {
- if (!strcmp(copybuf, "\\.")) {
- copydone = true;
- }
- firstload = false;
- }
- }
- PQputline(res->conn, "\n");
+ firstload = true;
+ linedone = false;
+ while (!linedone) { /* for each buffer ... */
+ s = copybuf;
+ buflen = COPYBUFSIZ;
+ for (; buflen > 1 &&
+ !(linedone = (c = getc(copystream)) == '\n' || c == EOF);
+ --buflen) {
+ *s++ = c;
+ }
+ if (c == EOF) {
+ PQputline(res->conn, "\\.");
+ copydone = true;
+ break;
+ }
+ *s = '\0';
+ PQputline(res->conn, copybuf);
+ if (firstload) {
+ if (!strcmp(copybuf, "\\.")) {
+ copydone = true;
+ }
+ firstload = false;
+ }
+ }
+ PQputline(res->conn, "\n");
}
PQendcopy(res->conn);
}
+
+
/* try to open fname and return a FILE *,
if it fails, use stdout, instead */
setFout(PsqlSettings *ps, char *fname)
{
if (ps->queryFout && ps->queryFout != stdout)
- {
- if (ps->pipe)
- pclose(ps->queryFout);
- else
- fclose(ps->queryFout);
- }
- if (!fname)
- ps->queryFout = stdout;
- else
- {
- if (*fname == '|')
- {
- signal(SIGPIPE, SIG_IGN);
- ps->queryFout = popen(fname+1, "w");
- ps->pipe = 1;
- }
- else
- {
- ps->queryFout = fopen(fname, "w");
- ps->pipe = 0;
- }
- if (!ps->queryFout) {
- perror(fname);
- ps->queryFout = stdout;
- }
- }
+ {
+ if (ps->pipe)
+ pclose(ps->queryFout);
+ else
+ fclose(ps->queryFout);
+ }
+ if (!fname)
+ ps->queryFout = stdout;
+ else
+ {
+ if (*fname == '|')
+ {
+ signal(SIGPIPE, SIG_IGN);
+ ps->queryFout = popen(fname+1, "w");
+ ps->pipe = 1;
+ }
+ else
+ {
+ ps->queryFout = fopen(fname, "w");
+ ps->pipe = 0;
+ }
+ if (!ps->queryFout) {
+ perror(fname);
+ ps->queryFout = stdout;
+ }
+ }
return ps->queryFout;
}