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

Add frontend \copy command.

parent 434201d8
/*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*
* 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 [<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);
......@@ -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 <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:
......@@ -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 <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':
......@@ -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 <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;
}
......@@ -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;
}
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