From: Bryan Henderson Date: Mon, 4 Nov 1996 09:17:55 +0000 (+0000) Subject: Add frontend \copy command. X-Git-Tag: REL2_0~257 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7870c5a0d32b6cbe0cd51c8c00c5c332a40e7a89;p=postgresql Add frontend \copy command. --- diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c index 98d678290d..5cf3762f41 100644 --- a/src/bin/psql/psql.c +++ b/src/bin/psql/psql.c @@ -1,4 +1,4 @@ -/*------------------------------------------------------------------------- +*------------------------------------------------------------------------- * * psql.c-- * an interactive front-end to postgres95 @@ -7,7 +7,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -24,7 +24,7 @@ #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 @@ -38,20 +38,20 @@ extern char *readline(char *); /* in rlstubs.c */ #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 */ @@ -62,8 +62,9 @@ typedef struct _psqlSettings { /* 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); @@ -71,16 +72,17 @@ char *gets_noreadline(char *prompt, FILE *source); 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); @@ -122,7 +124,7 @@ usage(char *progname) char *on(bool f) { - return f? "on": "off"; + return f? "on": "off"; } static void @@ -131,7 +133,8 @@ slashUsage(PsqlSettings *ps) fprintf(stderr,"\t \\? -- help\n"); fprintf(stderr,"\t \\a -- toggle field-alignment (currenty %s)\n", on(ps->opt.align)); fprintf(stderr,"\t \\C [] -- set html3 caption (currently '%s')\n", ps->opt.caption? ps->opt.caption: ""); - fprintf(stderr,"\t \\c -- connect to new database (currently '%s')\n", PQdb(ps->db)); + fprintf(stderr,"\t \\connect -- connect to new database (currently '%s')\n", PQdb(ps->db)); + fprintf(stderr,"\t \\copy -- copy table to/from a file\n"); fprintf(stderr,"\t \\d [] -- list tables in database or columns in
,* for all\n"); fprintf(stderr,"\t \\e [] -- edit the current query buffer or ,\\E execute too\n"); fprintf(stderr,"\t \\f [] -- change field separater (currently '%s')\n", ps->opt.fieldSep); @@ -157,19 +160,19 @@ slashUsage(PsqlSettings *ps) 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 @@ -192,7 +195,7 @@ listAllDbs(PsqlSettings *ps) { PQprint(ps->queryFout, results, - &ps->opt); + &ps->opt); PQclear(results); return 0; } @@ -226,55 +229,55 @@ tableList (PsqlSettings *ps, bool deep_tablelist) 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); @@ -316,7 +319,7 @@ tableDesc (PsqlSettings *ps, char *table) 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) @@ -348,21 +351,21 @@ tableDesc (PsqlSettings *ps, char *table) 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"); } @@ -423,7 +426,8 @@ gets_fromFile(char *prompt, FILE *source) 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; @@ -431,18 +435,19 @@ gets_fromFile(char *prompt, FILE *source) /* * 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); @@ -450,81 +455,102 @@ SendQuery(PsqlSettings *settings, char *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) @@ -533,12 +559,12 @@ 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); @@ -548,49 +574,360 @@ editFile(char *fname) 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 the value of string 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 where 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: @@ -599,7 +936,7 @@ decode(char *s) 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 @@ -607,287 +944,179 @@ decode(char *s) */ 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 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 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 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 where 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; iopt.html3, "HTML3.0 tablular output")) + if (toggle(settings, &settings->opt.html3, "HTML3.0 tabular output")) settings->opt.standard = 0; break; case 'o': @@ -896,8 +1125,8 @@ HandleSlashCmds(PsqlSettings *settings, case 'p': if (query) { - fputs(query, stdout); - fputc('\n', stdout); + fputs(query, stdout); + fputc('\n', stdout); } break; case 'q': /* \q is quit */ @@ -906,74 +1135,58 @@ HandleSlashCmds(PsqlSettings *settings, 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
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; } @@ -990,152 +1203,151 @@ HandleSlashCmds(PsqlSettings *settings, 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) { @@ -1164,26 +1376,26 @@ 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; @@ -1248,92 +1460,89 @@ main(int argc, char **argv) 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; @@ -1341,56 +1550,49 @@ handleCopyIn(PGresult *res, bool quiet) 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 */ @@ -1398,31 +1600,31 @@ FILE * 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; }