Commit 7870c5a0 authored by Bryan Henderson's avatar Bryan Henderson

Add frontend \copy command.

parent 434201d8
/*------------------------------------------------------------------------- *-------------------------------------------------------------------------
* *
* psql.c-- * psql.c--
* an interactive front-end to postgres95 * an interactive front-end to postgres95
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,8 +62,9 @@ typedef struct _psqlSettings { ...@@ -62,8 +62,9 @@ typedef struct _psqlSettings {
/* declarations for functions in this file */ /* declarations for functions in this file */
static void usage(char *progname); static void usage(char *progname);
static void slashUsage(); static void slashUsage();
static void handleCopyOut(PGresult *res, bool quiet); static void handleCopyOut(PGresult *res, bool quiet, FILE *copystream);
static void handleCopyIn(PGresult *res, bool quiet); static void handleCopyIn(PGresult *res, const bool mustprompt,
FILE *copystream);
static int tableList(PsqlSettings *ps, bool deep_tablelist); static int tableList(PsqlSettings *ps, bool deep_tablelist);
static int tableDesc(PsqlSettings *ps, char *table); static int tableDesc(PsqlSettings *ps, char *table);
...@@ -71,7 +72,8 @@ char *gets_noreadline(char *prompt, FILE *source); ...@@ -71,7 +72,8 @@ char *gets_noreadline(char *prompt, FILE *source);
char *gets_readline(char *prompt, FILE *source); char *gets_readline(char *prompt, FILE *source);
char *gets_fromFile(char *prompt, FILE *source); char *gets_fromFile(char *prompt, FILE *source);
int listAllDbs(PsqlSettings *settings); 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, int HandleSlashCmds(PsqlSettings *settings,
char *line, char *line,
char *query); char *query);
...@@ -131,7 +133,8 @@ slashUsage(PsqlSettings *ps) ...@@ -131,7 +133,8 @@ slashUsage(PsqlSettings *ps)
fprintf(stderr,"\t \\? -- help\n"); fprintf(stderr,"\t \\? -- help\n");
fprintf(stderr,"\t \\a -- toggle field-alignment (currenty %s)\n", on(ps->opt.align)); 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 [<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 \\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 \\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); fprintf(stderr,"\t \\f [<sep>] -- change field separater (currently '%s')\n", ps->opt.fieldSep);
...@@ -423,7 +426,8 @@ gets_fromFile(char *prompt, FILE *source) ...@@ -423,7 +426,8 @@ gets_fromFile(char *prompt, FILE *source)
len = strlen(line); len = strlen(line);
if (len == MAX_QUERY_BUFFER) 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; return line;
...@@ -431,18 +435,19 @@ gets_fromFile(char *prompt, FILE *source) ...@@ -431,18 +435,19 @@ gets_fromFile(char *prompt, FILE *source)
/* /*
* SendQuery: send the query string to the backend * SendQuery: send the query string to the backend
* return 0 if the query executed successfully * return *success_p = 1 if the query executed successfully
* returns 1 otherwise * returns *success_p = 0 otherwise
*/ */
int void
SendQuery(PsqlSettings *settings, char *query) SendQuery(bool *success_p, PsqlSettings *settings, const char *query,
{ const bool copy_in, const bool copy_out, FILE *copystream) {
PGresult *results; PGresult *results;
PGnotify *notify; PGnotify *notify;
int status = 0;
if (settings->singleStep) if (settings->singleStep)
fprintf(stdout, "\n*******************************************************************************\n"); fprintf(stdout, "\n**************************************"
"*****************************************\n");
if (settings->echoQuery || settings->singleStep) { if (settings->echoQuery || settings->singleStep) {
fprintf(stderr,"QUERY: %s\n",query); fprintf(stderr,"QUERY: %s\n",query);
...@@ -450,7 +455,8 @@ SendQuery(PsqlSettings *settings, char *query) ...@@ -450,7 +455,8 @@ SendQuery(PsqlSettings *settings, char *query)
} }
if (settings->singleStep) { if (settings->singleStep) {
fprintf(stdout, "\n*******************************************************************************\n"); fprintf(stdout, "\n**************************************"
"*****************************************\n");
fflush(stdout); fflush(stdout);
printf("\npress return to continue ..\n"); printf("\npress return to continue ..\n");
gets_fromFile("",stdin); gets_fromFile("",stdin);
...@@ -459,22 +465,19 @@ SendQuery(PsqlSettings *settings, char *query) ...@@ -459,22 +465,19 @@ SendQuery(PsqlSettings *settings, char *query)
results = PQexec(settings->db, query); results = PQexec(settings->db, query);
if (results == NULL) { if (results == NULL) {
fprintf(stderr,"%s",PQerrorMessage(settings->db)); fprintf(stderr,"%s",PQerrorMessage(settings->db));
return 1; *success_p = false;
} } else {
switch (PQresultStatus(results)) { switch (PQresultStatus(results)) {
case PGRES_TUPLES_OK: case PGRES_TUPLES_OK:
if (settings->gfname) if (settings->gfname) {
{
PsqlSettings ps=*settings; PsqlSettings ps=*settings;
FILE *fp; FILE *fp;
ps.queryFout=stdout; ps.queryFout=stdout;
fp=setFout(&ps, settings->gfname); fp=setFout(&ps, settings->gfname);
if (!fp || fp==stdout) if (!fp || fp==stdout) {
{ *success_p = false;
status = 1;
break; break;
} } else *success_p = true;
PQprint(fp, PQprint(fp,
results, results,
&(settings->opt)); &(settings->opt));
...@@ -484,8 +487,8 @@ SendQuery(PsqlSettings *settings, char *query) ...@@ -484,8 +487,8 @@ SendQuery(PsqlSettings *settings, char *query)
fclose(fp); fclose(fp);
settings->gfname=NULL; settings->gfname=NULL;
break; break;
} else } else {
{ *success_p = true;
PQprint(settings->queryFout, PQprint(settings->queryFout,
results, results,
&(settings->opt)); &(settings->opt));
...@@ -494,22 +497,44 @@ SendQuery(PsqlSettings *settings, char *query) ...@@ -494,22 +497,44 @@ SendQuery(PsqlSettings *settings, char *query)
PQclear(results); PQclear(results);
break; break;
case PGRES_EMPTY_QUERY: case PGRES_EMPTY_QUERY:
/* do nothing */ *success_p = true;
break; break;
case PGRES_COMMAND_OK: case PGRES_COMMAND_OK:
*success_p = true;
if (!settings->quiet) if (!settings->quiet)
fprintf(stdout,"%s\n", PQcmdStatus(results)); fprintf(stdout,"%s\n", PQcmdStatus(results));
break; break;
case PGRES_COPY_OUT: case PGRES_COPY_OUT:
handleCopyOut(results, settings->quiet); *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; break;
case PGRES_COPY_IN: case PGRES_COPY_IN:
handleCopyIn(results, settings->quiet); *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; break;
case PGRES_NONFATAL_ERROR: case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR: case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE: case PGRES_BAD_RESPONSE:
status = 1; *success_p = false;
fprintf(stderr,"%s",PQerrorMessage(settings->db)); fprintf(stderr,"%s",PQerrorMessage(settings->db));
break; break;
} }
...@@ -517,14 +542,15 @@ SendQuery(PsqlSettings *settings, char *query) ...@@ -517,14 +542,15 @@ SendQuery(PsqlSettings *settings, char *query)
/* check for asynchronous returns */ /* check for asynchronous returns */
notify = PQnotifies(settings->db); notify = PQnotifies(settings->db);
if (notify) { if (notify) {
fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n", fprintf(stderr,
"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
notify->relname, notify->be_pid); notify->relname, notify->be_pid);
free(notify); free(notify);
} }
}
}
return status;
}
void void
editFile(char *fname) editFile(char *fname)
...@@ -554,43 +580,354 @@ toggle(PsqlSettings *settings, bool *sw, char *msg) ...@@ -554,43 +580,354 @@ toggle(PsqlSettings *settings, bool *sw, char *msg)
return *sw; return *sw;
} }
void void
decode(char *s) unescape(char *dest, const char *source) {
{ /*-----------------------------------------------------------------------------
char *p, *d; Return as the string <dest> the value of string <source> with escape
bool esc=0; sequences turned into the bytes they represent.
for (d=p=s; *p; p++) -----------------------------------------------------------------------------*/
{ char *p;
char c=*p; bool esc; /* Last character we saw was the escape character (/) */
if (esc)
{ esc = false; /* Haven't seen escape character yet */
switch(*p) for (p = (char *)source; *p; p++) {
{ char c; /* Our output character */
if (esc) {
switch(*p) {
case 'n': case 'n':
c='\n'; c = '\n';
break; break;
case 'r': case 'r':
c='\r'; c = '\r';
break; break;
case 't': case 't':
c='\t'; c = '\t';
break; break;
case 'f': case 'f':
c='\f'; c = '\f';
break;
case '\\':
c = '\\';
break; break;
default:
c = *p;
} }
esc=0; esc = false;
} else } else
if (c=='\\') if (*p == '\\') {
{ esc = true;
esc=1; c = ' '; /* meaningless, but compiler doesn't know that */
continue; } else {
c = *p;
esc = false;
} }
*d++=c; if (!esc) *dest ++= c;
} }
*d='\0'; *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: HandleSlashCmds:
...@@ -611,16 +948,45 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -611,16 +948,45 @@ HandleSlashCmds(PsqlSettings *settings,
char *query) char *query)
{ {
int status = 1; int status = 1;
char *optarg = NULL; char *optarg;
int len; /* 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 */
len = strlen(line); cmd = malloc(strlen(line)); /* unescaping better not make string grow. */
if (len > 2)
{ unescape(cmd, line+1); /* sets cmd string */
optarg = leftTrim(line+2);
decode(optarg); /* Originally, there were just single character commands. Now,
} we define some longer, friendly commands, but we have to keep
switch (line[1]) 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'".
*/
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 */ case 'a': /* toggles to align fields on output */
toggle(settings, &settings->opt.align, "field alignment"); toggle(settings, &settings->opt.align, "field alignment");
...@@ -637,37 +1003,13 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -637,37 +1003,13 @@ HandleSlashCmds(PsqlSettings *settings,
exit(1); exit(1);
} }
break; break;
case 'c': /* \c means connect to new database */ case 'c': {
{ if (strncmp(cmd, "copy ", strlen("copy ")) == 0)
char *dbname=PQdb(settings->db); do_copy(optarg2, settings);
if (!optarg) { else if (strncmp(cmd, "connect ", strlen("connect ")) == 0)
fprintf(stderr,"\\c must be followed by a database name\n"); do_connect(optarg2, settings);
break; else
} do_connect(optarg, settings);
{
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;
}
} }
break; break;
case 'd': /* \d describe tables or columns in a table */ case 'd': /* \d describe tables or columns in a table */
...@@ -683,62 +1025,9 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -683,62 +1025,9 @@ HandleSlashCmds(PsqlSettings *settings,
tableDesc(settings, optarg); tableDesc(settings, optarg);
} }
break; break;
case 'e': case 'e': /* edit */
{ {
int fd; do_edit(optarg, query, &status);
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; break;
} }
case 'E': case 'E':
...@@ -800,69 +1089,9 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -800,69 +1089,9 @@ HandleSlashCmds(PsqlSettings *settings,
settings->gfname = optarg; settings->gfname = optarg;
status = 0; status = 0;
break; 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': do_help(optarg);
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; break;
} }
case 'i': /* \i is include file */ case 'i': /* \i is include file */
...@@ -887,7 +1116,7 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -887,7 +1116,7 @@ HandleSlashCmds(PsqlSettings *settings,
listAllDbs(settings); listAllDbs(settings);
break; break;
case 'H': 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; settings->opt.standard = 0;
break; break;
case 'o': case 'o':
...@@ -950,30 +1179,14 @@ HandleSlashCmds(PsqlSettings *settings, ...@@ -950,30 +1179,14 @@ HandleSlashCmds(PsqlSettings *settings,
toggle(settings, &settings->opt.expanded, "expanded table representation"); toggle(settings, &settings->opt.expanded, "expanded table representation");
break; break;
case '!': case '!':
if (!optarg) { do_shell(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);
break; break;
default: default:
case '?': /* \? is help */ case '?': /* \? is help */
slashUsage(settings); slashUsage(settings);
break; break;
} }
free(cmd);
return status; return status;
} }
...@@ -1015,7 +1228,8 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1015,7 +1228,8 @@ MainLoop(PsqlSettings *settings, FILE *source)
if (interactive) { if (interactive) {
if (settings->prompt) if (settings->prompt)
free(settings->prompt); free(settings->prompt);
settings->prompt = malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1); settings->prompt =
malloc(strlen(PQdb(settings->db)) + strlen(PROMPT) + 1);
if (settings->quiet) if (settings->quiet)
settings->prompt[0] = '\0'; settings->prompt[0] = '\0';
else else
...@@ -1023,12 +1237,8 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1023,12 +1237,8 @@ MainLoop(PsqlSettings *settings, FILE *source)
if (settings->useReadline) { if (settings->useReadline) {
using_history(); using_history();
GetNextLine = gets_readline; GetNextLine = gets_readline;
} else } else GetNextLine = gets_noreadline;
GetNextLine = gets_noreadline; } else GetNextLine = gets_fromFile;
}
else
GetNextLine = gets_fromFile;
query[0] = '\0'; query[0] = '\0';
...@@ -1037,10 +1247,11 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1037,10 +1247,11 @@ MainLoop(PsqlSettings *settings, FILE *source)
line = GetNextLine(settings->prompt, source); line = GetNextLine(settings->prompt, source);
if (line == NULL) { /* No more input. Time to quit */ if (line == NULL) { /* No more input. Time to quit */
printf("EOF\n"); /* Goes on prompt line */ printf("EOF\n"); /* Goes on prompt line */
eof = 1; eof = true;
} else { } else {
exitStatus = 0; exitStatus = 0;
line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */ line = rightTrim(line);
/* remove whitespaces on the right, incl. \n's */
if (line[0] == '\0') { if (line[0] == '\0') {
free(line); free(line);
...@@ -1049,16 +1260,18 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1049,16 +1260,18 @@ MainLoop(PsqlSettings *settings, FILE *source)
/* filter out comment lines that begin with --, /* filter out comment lines that begin with --,
this could be incorrect if -- is part of a quoted string. 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 But we won't go through the trouble of detecting that.
-- in your quoted string, be careful and don't start a line with it */ If you have -- in your quoted string, be careful and don't
start a line with it
*/
if (line[0] == '-' && line[1] == '-') { if (line[0] == '-' && line[1] == '-') {
if (settings->singleStep) /* in single step mode, show comments */ if (settings->singleStep)
/* in single step mode, show comments */
fprintf(stdout,"%s\n",line); fprintf(stdout,"%s\n",line);
free(line); free(line);
continue; continue;
} }
if (line[0] != '\\' && querySent) if (line[0] != '\\' && querySent) {
{
query[0]='\0'; query[0]='\0';
querySent = 0; querySent = 0;
} }
...@@ -1069,16 +1282,15 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1069,16 +1282,15 @@ MainLoop(PsqlSettings *settings, FILE *source)
add_history(line); /* save non-empty lines in history */ add_history(line); /* save non-empty lines in history */
/* do the query immediately if we are doing single line queries /* do the query immediately if we are doing single line queries
or if the last character is a semicolon */ or if the last character is a semicolon
*/
sendQuery = settings->singleLineMode || (line[len-1] == ';') ; sendQuery = settings->singleLineMode || (line[len-1] == ';') ;
/* normally, \ commands have to be start the line, /* normally, \ commands have to be start the line,
but for backwards compatibility with monitor, but for backwards compatibility with monitor,
check for \g at the end of line */ check for \g at the end of line */
if (len > 2 && !sendQuery) if (len > 2 && !sendQuery) {
{ if (line[len-1]=='g' && line[len-2]=='\\') {
if (line[len-1]=='g' && line[len-2]=='\\')
{
sendQuery = 1; sendQuery = 1;
line[len-2]='\0'; line[len-2]='\0';
} }
...@@ -1099,35 +1311,33 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1099,35 +1311,33 @@ MainLoop(PsqlSettings *settings, FILE *source)
} }
if (slashCmdStatus == 0) if (slashCmdStatus == 0)
sendQuery = 1; sendQuery = 1;
} } else if (strlen(query) + len > MAX_QUERY_BUFFER) {
else fprintf(stderr,"query buffer max length of %d exceeded\n",
if (strlen(query) + len > MAX_QUERY_BUFFER) MAX_QUERY_BUFFER);
{
fprintf(stderr,"query buffer max length of %d exceeded\n",MAX_QUERY_BUFFER);
fprintf(stderr,"query line ignored\n"); fprintf(stderr,"query line ignored\n");
} } else if (query[0]!='\0') {
else
if (query[0]!='\0') {
strcat(query,"\n"); strcat(query,"\n");
strcat(query,line); strcat(query,line);
} } else strcpy(query,line);
else if (sendQuery && query[0] != '\0') {
strcpy(query,line);
if (sendQuery && query[0] != '\0')
{
/* echo the line read from the file, /* echo the line read from the file,
unless we are in single_step mode, because single_step mode unless we are in single_step mode, because single_step mode
will echo anyway */ will echo anyway
*/
bool success; /* The query succeeded at the backend */
if (!interactive && !settings->singleStep && !settings->quiet) if (!interactive && !settings->singleStep && !settings->quiet)
fprintf(stderr,"%s\n", query); fprintf(stderr,"%s\n", query);
exitStatus = SendQuery(settings, query); SendQuery(&success, settings, query, false, false, 0);
exitStatus = success ? 0 : 1;
querySent = 1; querySent = 1;
if (PQstatus(settings->db) == CONNECTION_BAD) { if (PQstatus(settings->db) == CONNECTION_BAD) {
connected = 0; connected = 0;
fprintf(stderr, "We have lost the connection to the backend, so " fprintf(stderr,
"further processing is impossible. Terminating.\n"); "We have lost the connection to the backend, so "
"further processing is impossible. "
"Terminating.\n");
} }
} }
free(line); /* free storage malloc'd by GetNextLine */ free(line); /* free storage malloc'd by GetNextLine */
...@@ -1136,6 +1346,8 @@ MainLoop(PsqlSettings *settings, FILE *source) ...@@ -1136,6 +1346,8 @@ MainLoop(PsqlSettings *settings, FILE *source)
return exitStatus; return exitStatus;
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
...@@ -1258,7 +1470,8 @@ main(int argc, char **argv) ...@@ -1258,7 +1470,8 @@ main(int argc, char **argv)
if (!settings.quiet && !singleQuery && !qfilename) { if (!settings.quiet && !singleQuery && !qfilename) {
printf("Welcome to the POSTGRES95 interactive sql monitor:\n"); printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
printf(" Please read the file COPYRIGHT for copyright terms of POSTGRES95\n\n"); printf(" Please read the file COPYRIGHT for copyright terms "
"of POSTGRES95\n\n");
printf(" type \\? for help on slash commands\n"); printf(" type \\? for help on slash commands\n");
printf(" type \\q to quit\n"); printf(" type \\q to quit\n");
printf(" type \\g or terminate with semicolon to execute query\n"); printf(" type \\g or terminate with semicolon to execute query\n");
...@@ -1273,19 +1486,17 @@ main(int argc, char **argv) ...@@ -1273,19 +1486,17 @@ main(int argc, char **argv)
if ( singleSlashCmd ) { if ( singleSlashCmd ) {
/* Not really a query, but "Do what I mean, not what I say." */ /* Not really a query, but "Do what I mean, not what I say." */
line = singleQuery; line = singleQuery;
} } else {
else {
line = malloc(strlen(qfilename) + 5); line = malloc(strlen(qfilename) + 5);
sprintf(line,"\\i %s", qfilename); sprintf(line,"\\i %s", qfilename);
} }
HandleSlashCmds(&settings, line, ""); HandleSlashCmds(&settings, line, "");
} else { } else {
if (singleQuery) { if (singleQuery) {
exitStatus = SendQuery(&settings, singleQuery); bool success; /* The query succeeded at the backend */
} SendQuery(&success, &settings, singleQuery, false, false, 0);
else exitStatus = success ? 0 : 1;
exitStatus = MainLoop(&settings, stdin); } else exitStatus = MainLoop(&settings, stdin);
} }
PQfinish(settings.db); PQfinish(settings.db);
...@@ -1296,14 +1507,12 @@ main(int argc, char **argv) ...@@ -1296,14 +1507,12 @@ main(int argc, char **argv)
#define COPYBUFSIZ 8192 #define COPYBUFSIZ 8192
static void static void
handleCopyOut(PGresult *res, bool quiet) handleCopyOut(PGresult *res, bool quiet, FILE *copystream) {
{ bool copydone;
bool copydone = false;
char copybuf[COPYBUFSIZ]; char copybuf[COPYBUFSIZ];
int ret; int ret;
if (!quiet) copydone = false; /* Can't be done; haven't started. */
fprintf(stdout, "Copy command returns...\n");
while (!copydone) { while (!copydone) {
ret = PQgetline(res->conn, copybuf, COPYBUFSIZ); ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
...@@ -1313,27 +1522,27 @@ handleCopyOut(PGresult *res, bool quiet) ...@@ -1313,27 +1522,27 @@ handleCopyOut(PGresult *res, bool quiet)
copybuf[2] =='\0') { copybuf[2] =='\0') {
copydone = true; /* don't print this... */ copydone = true; /* don't print this... */
} else { } else {
fputs(copybuf, stdout); fputs(copybuf, copystream);
switch (ret) { switch (ret) {
case EOF: case EOF:
copydone = true; copydone = true;
/*FALLTHROUGH*/ /*FALLTHROUGH*/
case 0: case 0:
fputc('\n', stdout); fputc('\n', copystream);
break; break;
case 1: case 1:
break; break;
} }
} }
} }
fflush(stdout); fflush(copystream);
PQendcopy(res->conn); PQendcopy(res->conn);
} }
static void static void
handleCopyIn(PGresult *res, bool quiet) handleCopyIn(PGresult *res, const bool mustprompt, FILE *copystream) {
{
bool copydone = false; bool copydone = false;
bool firstload; bool firstload;
bool linedone; bool linedone;
...@@ -1342,22 +1551,14 @@ handleCopyIn(PGresult *res, bool quiet) ...@@ -1342,22 +1551,14 @@ handleCopyIn(PGresult *res, bool quiet)
int buflen; int buflen;
int c; int c;
if (!quiet) { if (mustprompt) {
fputs("Enter info followed by a newline\n", stdout); fputs("Enter info followed by a newline\n", stdout);
fputs("End with a backslash and a period on a line by itself.\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 ... */ while (!copydone) { /* for each input line ... */
if (!quiet) { if (mustprompt) {
fputs(">> ", stdout); fputs(">> ", stdout);
fflush(stdout); fflush(stdout);
} }
...@@ -1367,12 +1568,11 @@ handleCopyIn(PGresult *res, bool quiet) ...@@ -1367,12 +1568,11 @@ handleCopyIn(PGresult *res, bool quiet)
s = copybuf; s = copybuf;
buflen = COPYBUFSIZ; buflen = COPYBUFSIZ;
for (; buflen > 1 && for (; buflen > 1 &&
!(linedone = (c = getc(stdin)) == '\n' || c == EOF); !(linedone = (c = getc(copystream)) == '\n' || c == EOF);
--buflen) { --buflen) {
*s++ = c; *s++ = c;
} }
if (c == EOF) { if (c == EOF) {
/* reading from stdin, but from a file */
PQputline(res->conn, "\\."); PQputline(res->conn, "\\.");
copydone = true; copydone = true;
break; break;
...@@ -1391,6 +1591,8 @@ handleCopyIn(PGresult *res, bool quiet) ...@@ -1391,6 +1591,8 @@ handleCopyIn(PGresult *res, bool quiet)
PQendcopy(res->conn); PQendcopy(res->conn);
} }
/* try to open fname and return a FILE *, /* try to open fname and return a FILE *,
if it fails, use stdout, instead */ if it fails, use stdout, instead */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment