Commit 0e6652e6 authored by Bruce Momjian's avatar Bruce Momjian

psql cleanup

parent 2323b636
...@@ -35,21 +35,20 @@ ...@@ -35,21 +35,20 @@
/* functions for use in this file only */ /* functions for use in this file only */
static backslashResult static backslashResult exec_command(const char *cmd,
exec_command(const char * cmd, char *const * options,
char * const * options, const char *options_string,
const char * options_string,
PQExpBuffer query_buf, PQExpBuffer query_buf,
PsqlSettings * pset); PsqlSettings *pset);
static bool static bool
do_edit(const char *filename_arg, PQExpBuffer query_buf); do_edit(const char *filename_arg, PQExpBuffer query_buf);
static char * static char *
unescape(const char * source, PsqlSettings * pset); unescape(const char *source, PsqlSettings *pset);
static bool static bool
do_shell(const char *command); do_shell(const char *command);
...@@ -74,42 +73,50 @@ backslashResult ...@@ -74,42 +73,50 @@ backslashResult
HandleSlashCmds(PsqlSettings *pset, HandleSlashCmds(PsqlSettings *pset,
const char *line, const char *line,
PQExpBuffer query_buf, PQExpBuffer query_buf,
const char ** end_of_cmd) const char **end_of_cmd)
{ {
backslashResult status = CMD_SKIP_LINE; backslashResult status = CMD_SKIP_LINE;
char * my_line; char *my_line;
char * options[17] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; char *options[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char * token; char *token;
const char * options_string = NULL; const char *options_string = NULL;
const char * cmd; const char *cmd;
size_t blank_loc; size_t blank_loc;
int i; int i;
const char * continue_parse = NULL; /* tell the mainloop where the backslash command ended */ const char *continue_parse = NULL; /* tell the mainloop where the
* backslash command ended */
my_line = xstrdup(line); my_line = xstrdup(line);
/* Find the first whitespace (or backslash) /*
line[blank_loc] will now be the whitespace character * Find the first whitespace (or backslash) line[blank_loc] will now
or the \0 at the end */ * be the whitespace character or the \0 at the end
*/
blank_loc = strcspn(my_line, " \t"); blank_loc = strcspn(my_line, " \t");
/* do we have an option string? */ /* do we have an option string? */
if (my_line[blank_loc] != '\0') { if (my_line[blank_loc] != '\0')
options_string = &my_line[blank_loc+1]; {
options_string = &my_line[blank_loc + 1];
my_line[blank_loc] = '\0'; my_line[blank_loc] = '\0';
} }
if (options_string) { if (options_string)
{
char quote; char quote;
unsigned int pos; unsigned int pos;
options_string = &options_string[strspn(options_string, " \t")]; /* skip leading whitespace */
options_string = &options_string[strspn(options_string, " \t")]; /* skip leading
* whitespace */
i = 0; i = 0;
token = strtokx(options_string, " \t", "\"'`", '\\', &quote, &pos); token = strtokx(options_string, " \t", "\"'`", '\\', &quote, &pos);
for (i = 0; token && i<16; i++) { for (i = 0; token && i < 16; i++)
switch(quote) { {
switch (quote)
{
case '"': case '"':
options[i] = unescape(token, pset); options[i] = unescape(token, pset);
break; break;
...@@ -119,24 +126,28 @@ HandleSlashCmds(PsqlSettings *pset, ...@@ -119,24 +126,28 @@ HandleSlashCmds(PsqlSettings *pset,
case '`': case '`':
{ {
bool error = false; bool error = false;
FILE * fd = NULL; FILE *fd = NULL;
char * file = unescape(token, pset); char *file = unescape(token, pset);
PQExpBufferData output; PQExpBufferData output;
char buf[512]; char buf[512];
size_t result; size_t result;
fd = popen(file, "r"); fd = popen(file, "r");
if (!fd) { if (!fd)
{
perror(file); perror(file);
error = true; error = true;
} }
if (!error) { if (!error)
{
initPQExpBuffer(&output); initPQExpBuffer(&output);
do { do
{
result = fread(buf, 1, 512, fd); result = fread(buf, 1, 512, fd);
if (ferror(fd)) { if (ferror(fd))
{
perror(file); perror(file);
error = true; error = true;
break; break;
...@@ -145,21 +156,24 @@ HandleSlashCmds(PsqlSettings *pset, ...@@ -145,21 +156,24 @@ HandleSlashCmds(PsqlSettings *pset,
} while (!feof(fd)); } while (!feof(fd));
appendPQExpBufferChar(&output, '\0'); appendPQExpBufferChar(&output, '\0');
if (pclose(fd) == -1) { if (pclose(fd) == -1)
{
perror(file); perror(file);
error = true; error = true;
} }
} }
if (!error) { if (!error)
if (output.data[strlen(output.data)-1] == '\n') {
output.data[strlen(output.data)-1] = '\0'; if (output.data[strlen(output.data) - 1] == '\n')
output.data[strlen(output.data) - 1] = '\0';
} }
free(file); free(file);
if (!error) if (!error)
options[i] = output.data; options[i] = output.data;
else { else
{
options[i] = xstrdup(""); options[i] = xstrdup("");
termPQExpBuffer(&output); termPQExpBuffer(&output);
} }
...@@ -170,7 +184,7 @@ HandleSlashCmds(PsqlSettings *pset, ...@@ -170,7 +184,7 @@ HandleSlashCmds(PsqlSettings *pset,
if (token[0] == '\\') if (token[0] == '\\')
continue_parse = options_string + pos; continue_parse = options_string + pos;
else if (token[0] == '$') else if (token[0] == '$')
options[i] = xstrdup(interpolate_var(token+1, pset)); options[i] = xstrdup(interpolate_var(token + 1, pset));
else else
options[i] = xstrdup(token); options[i] = xstrdup(token);
break; break;
...@@ -187,35 +201,41 @@ HandleSlashCmds(PsqlSettings *pset, ...@@ -187,35 +201,41 @@ HandleSlashCmds(PsqlSettings *pset,
status = exec_command(cmd, options, options_string, query_buf, pset); status = exec_command(cmd, options, options_string, query_buf, pset);
if (status == CMD_UNKNOWN) { if (status == CMD_UNKNOWN)
/* If the command was not recognized, try inserting a space after {
the first letter and call again. The one letter commands
allow arguments to start immediately after the command, /*
but that is no longer encouraged. */ * If the command was not recognized, try inserting a space after
const char * new_options[17]; * the first letter and call again. The one letter commands allow
* arguments to start immediately after the command, but that is
* no longer encouraged.
*/
const char *new_options[17];
char new_cmd[2]; char new_cmd[2];
int i; int i;
for (i=1; i<17; i++) for (i = 1; i < 17; i++)
new_options[i] = options[i-1]; new_options[i] = options[i - 1];
new_options[0] = cmd+1; new_options[0] = cmd + 1;
new_cmd[0] = cmd[0]; new_cmd[0] = cmd[0];
new_cmd[1] = '\0'; new_cmd[1] = '\0';
status = exec_command(new_cmd, (char * const *)new_options, my_line+2, query_buf, pset); status = exec_command(new_cmd, (char *const *) new_options, my_line + 2, query_buf, pset);
} }
if (status == CMD_UNKNOWN) { if (status == CMD_UNKNOWN)
{
fprintf(stderr, "Unrecognized command: \\%s. Try \\? for help.\n", cmd); fprintf(stderr, "Unrecognized command: \\%s. Try \\? for help.\n", cmd);
status = CMD_ERROR; status = CMD_ERROR;
} }
if (continue_parse && *(continue_parse+1) == '\\') if (continue_parse && *(continue_parse + 1) == '\\')
continue_parse+=2; continue_parse += 2;
if (end_of_cmd) { if (end_of_cmd)
{
if (continue_parse) if (continue_parse)
*end_of_cmd = line + (continue_parse - my_line); *end_of_cmd = line + (continue_parse - my_line);
else else
...@@ -223,7 +243,7 @@ HandleSlashCmds(PsqlSettings *pset, ...@@ -223,7 +243,7 @@ HandleSlashCmds(PsqlSettings *pset,
} }
/* clean up */ /* clean up */
for (i = 0; i<16 && options[i]; i++) for (i = 0; i < 16 && options[i]; i++)
free(options[i]); free(options[i]);
free(my_line); free(my_line);
...@@ -235,21 +255,25 @@ HandleSlashCmds(PsqlSettings *pset, ...@@ -235,21 +255,25 @@ HandleSlashCmds(PsqlSettings *pset,
static backslashResult static backslashResult
exec_command(const char * cmd, exec_command(const char *cmd,
char * const * options, char *const * options,
const char * options_string, const char *options_string,
PQExpBuffer query_buf, PQExpBuffer query_buf,
PsqlSettings * pset) PsqlSettings *pset)
{ {
bool success = true; /* indicate here if the command ran ok or failed */ bool success = true; /* indicate here if the command ran ok or
* failed */
bool quiet = GetVariableBool(pset->vars, "quiet"); bool quiet = GetVariableBool(pset->vars, "quiet");
backslashResult status = CMD_SKIP_LINE; backslashResult status = CMD_SKIP_LINE;
/* \a -- toggle field alignment /*
This is deprecated and makes no sense, but we keep it around. */ * \a -- toggle field alignment This is deprecated and makes no sense,
if (strcmp(cmd, "a") == 0) { * but we keep it around.
*/
if (strcmp(cmd, "a") == 0)
{
if (pset->popt.topt.format != PRINT_ALIGNED) if (pset->popt.topt.format != PRINT_ALIGNED)
success = do_pset("format", "aligned", &pset->popt, quiet); success = do_pset("format", "aligned", &pset->popt, quiet);
else else
...@@ -257,30 +281,36 @@ exec_command(const char * cmd, ...@@ -257,30 +281,36 @@ exec_command(const char * cmd,
} }
/* \C -- override table title /*
(formerly change HTML caption) This is deprecated. */ * \C -- override table title (formerly change HTML caption) This is
* deprecated.
*/
else if (strcmp(cmd, "C") == 0) else if (strcmp(cmd, "C") == 0)
success = do_pset("title", options[0], &pset->popt, quiet); success = do_pset("title", options[0], &pset->popt, quiet);
/* \c or \connect -- connect to new database or as different user /*
* \c or \connect -- connect to new database or as different user
* *
* \c foo bar : connect to db "foo" as user "bar" * \c foo bar : connect to db "foo" as user "bar" \c foo [-] :
* \c foo [-] : connect to db "foo" as current user * connect to db "foo" as current user \c - bar : connect to
* \c - bar : connect to current db as user "bar" * current db as user "bar" \c : connect to default db as
* \c : connect to default db as default user * default user
*/ */
else if (strcmp(cmd, "c")==0 || strcmp(cmd, "connect")==0) else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
{ {
if (options[1]) if (options[1])
/* gave username */ /* gave username */
success = do_connect(options[0], options[1], pset); success = do_connect(options[0], options[1], pset);
else { else
{
if (options[0]) if (options[0])
/* gave database name */ /* gave database name */
success = do_connect(options[0], "", pset); /* empty string is same username as before, success = do_connect(options[0], "", pset); /* empty string is same
NULL would mean libpq default */ * username as before,
* NULL would mean libpq
* default */
else else
/* connect to default db as default user */ /* connect to default db as default user */
success = do_connect(NULL, NULL, pset); success = do_connect(NULL, NULL, pset);
...@@ -297,13 +327,16 @@ exec_command(const char * cmd, ...@@ -297,13 +327,16 @@ exec_command(const char * cmd,
print_copyright(); print_copyright();
/* \d* commands */ /* \d* commands */
else if (cmd[0] == 'd') { else if (cmd[0] == 'd')
switch(cmd[1]) { {
switch (cmd[1])
{
case '\0': case '\0':
if (options[0]) if (options[0])
success = describeTableDetails(options[0], pset); success = describeTableDetails(options[0], pset);
else else
success = listTables("tvs", NULL, pset); /* standard listing of interesting things */ success = listTables("tvs", NULL, pset); /* standard listing of
* interesting things */
break; break;
case 'a': case 'a':
success = describeAggregates(options[0], pset); success = describeAggregates(options[0], pset);
...@@ -326,7 +359,11 @@ exec_command(const char * cmd, ...@@ -326,7 +359,11 @@ exec_command(const char * cmd,
case 'T': case 'T':
success = describeTypes(options[0], pset); success = describeTypes(options[0], pset);
break; break;
case 't': case 'v': case 'i': case 's': case 'S': case 't':
case 'v':
case 'i':
case 's':
case 'S':
if (cmd[1] == 'S' && cmd[2] == '\0') if (cmd[1] == 'S' && cmd[2] == '\0')
success = listTables("Stvs", NULL, pset); success = listTables("Stvs", NULL, pset);
else else
...@@ -338,28 +375,35 @@ exec_command(const char * cmd, ...@@ -338,28 +375,35 @@ exec_command(const char * cmd,
} }
/* \e or \edit -- edit the current query buffer (or a file and make it the /*
query buffer */ * \e or \edit -- edit the current query buffer (or a file and make it
* the query buffer
*/
else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0) else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
status = do_edit(options[0], query_buf) ? CMD_NEWEDIT : CMD_ERROR; status = do_edit(options[0], query_buf) ? CMD_NEWEDIT : CMD_ERROR;
/* \echo */ /* \echo */
else if (strcmp(cmd, "echo") == 0) { else if (strcmp(cmd, "echo") == 0)
{
int i; int i;
for (i=0; i<16 && options[i]; i++)
for (i = 0; i < 16 && options[i]; i++)
fputs(options[i], stdout); fputs(options[i], stdout);
fputs("\n", stdout); fputs("\n", stdout);
} }
/* \f -- change field separator /*
(This is deprecated in favour of \pset.) */ * \f -- change field separator (This is deprecated in favour of
* \pset.)
*/
else if (strcmp(cmd, "f") == 0) else if (strcmp(cmd, "f") == 0)
success = do_pset("fieldsep", options[0], &pset->popt, quiet); success = do_pset("fieldsep", options[0], &pset->popt, quiet);
/* \g means send query */ /* \g means send query */
else if (strcmp(cmd, "g") == 0) { else if (strcmp(cmd, "g") == 0)
{
if (!options[0]) if (!options[0])
pset->gfname = NULL; pset->gfname = NULL;
else else
...@@ -380,7 +424,8 @@ exec_command(const char * cmd, ...@@ -380,7 +424,8 @@ exec_command(const char * cmd,
/* \i is include file */ /* \i is include file */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0) else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
{ {
if (!options[0]) { if (!options[0])
{
fputs("Usage: \\i <filename>\n", stderr); fputs("Usage: \\i <filename>\n", stderr);
success = false; success = false;
} }
...@@ -394,9 +439,12 @@ exec_command(const char * cmd, ...@@ -394,9 +439,12 @@ exec_command(const char * cmd,
/* large object things */ /* large object things */
else if (strncmp(cmd, "lo_", 3)==0) { else if (strncmp(cmd, "lo_", 3) == 0)
if (strcmp(cmd+3, "export") == 0) { {
if (!options[1]) { if (strcmp(cmd + 3, "export") == 0)
{
if (!options[1])
{
fputs("Usage: \\lo_export <loid> <filename>\n", stderr); fputs("Usage: \\lo_export <loid> <filename>\n", stderr);
success = false; success = false;
} }
...@@ -404,8 +452,10 @@ exec_command(const char * cmd, ...@@ -404,8 +452,10 @@ exec_command(const char * cmd,
success = do_lo_export(pset, options[0], options[1]); success = do_lo_export(pset, options[0], options[1]);
} }
else if (strcmp(cmd+3, "import") == 0) { else if (strcmp(cmd + 3, "import") == 0)
if (!options[0]) { {
if (!options[0])
{
fputs("Usage: \\lo_import <filename> [<description>]\n", stderr); fputs("Usage: \\lo_import <filename> [<description>]\n", stderr);
success = false; success = false;
} }
...@@ -413,11 +463,13 @@ exec_command(const char * cmd, ...@@ -413,11 +463,13 @@ exec_command(const char * cmd,
success = do_lo_import(pset, options[0], options[1]); success = do_lo_import(pset, options[0], options[1]);
} }
else if (strcmp(cmd+3, "list") == 0) else if (strcmp(cmd + 3, "list") == 0)
success = do_lo_list(pset); success = do_lo_list(pset);
else if (strcmp(cmd+3, "unlink") == 0) { else if (strcmp(cmd + 3, "unlink") == 0)
if (!options[0]) { {
if (!options[0])
{
fputs("Usage: \\lo_unlink <loid>\n", stderr); fputs("Usage: \\lo_unlink <loid>\n", stderr);
success = false; success = false;
} }
...@@ -435,7 +487,7 @@ exec_command(const char * cmd, ...@@ -435,7 +487,7 @@ exec_command(const char * cmd,
/* \p prints the current query buffer */ /* \p prints the current query buffer */
else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0 ) else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
{ {
if (query_buf && query_buf->len > 0) if (query_buf && query_buf->len > 0)
puts(query_buf->data); puts(query_buf->data);
...@@ -444,8 +496,10 @@ exec_command(const char * cmd, ...@@ -444,8 +496,10 @@ exec_command(const char * cmd,
} }
/* \pset -- set printing parameters */ /* \pset -- set printing parameters */
else if (strcmp(cmd, "pset")==0) { else if (strcmp(cmd, "pset") == 0)
if (!options[0]) { {
if (!options[0])
{
fputs("Usage: \\pset <parameter> [<value>]\n", stderr); fputs("Usage: \\pset <parameter> [<value>]\n", stderr);
success = false; success = false;
} }
...@@ -454,29 +508,33 @@ exec_command(const char * cmd, ...@@ -454,29 +508,33 @@ exec_command(const char * cmd,
} }
/* \q or \quit */ /* \q or \quit */
else if (strcmp(cmd, "q")==0 || strcmp(cmd, "quit")==0) else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0)
status = CMD_TERMINATE; status = CMD_TERMINATE;
/* \qecho */ /* \qecho */
else if (strcmp(cmd, "qecho") == 0) { else if (strcmp(cmd, "qecho") == 0)
{
int i; int i;
for (i=0; i<16 && options[i]; i++)
for (i = 0; i < 16 && options[i]; i++)
fputs(options[i], pset->queryFout); fputs(options[i], pset->queryFout);
fputs("\n", pset->queryFout); fputs("\n", pset->queryFout);
} }
/* reset(clear) the buffer */ /* reset(clear) the buffer */
else if (strcmp(cmd, "r")==0 || strcmp(cmd, "reset")==0) else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
{ {
resetPQExpBuffer(query_buf); resetPQExpBuffer(query_buf);
if (!quiet) puts("Query buffer reset (cleared)."); if (!quiet)
puts("Query buffer reset (cleared).");
} }
/* \s save history in a file or show it on the screen */ /* \s save history in a file or show it on the screen */
else if (strcmp(cmd, "s")==0) else if (strcmp(cmd, "s") == 0)
{ {
const char * fname; const char *fname;
if (!options[0]) if (!options[0])
fname = "/dev/tty"; fname = "/dev/tty";
else else
...@@ -490,19 +548,26 @@ exec_command(const char * cmd, ...@@ -490,19 +548,26 @@ exec_command(const char * cmd,
/* \set -- generalized set option command */ /* \set -- generalized set option command */
else if (strcmp(cmd, "set")==0) else if (strcmp(cmd, "set") == 0)
{
if (!options[0])
{ {
if (!options[0]) {
/* list all variables */ /* list all variables */
/* (This is in utter violation of the GetVariable abstraction, but
I have not dreamt up a better way.) */ /*
struct _variable * ptr; * (This is in utter violation of the GetVariable abstraction,
* but I have not dreamt up a better way.)
*/
struct _variable *ptr;
for (ptr = pset->vars; ptr->next; ptr = ptr->next) for (ptr = pset->vars; ptr->next; ptr = ptr->next)
fprintf(stdout, "%s = '%s'\n", ptr->next->name, ptr->next->value); fprintf(stdout, "%s = '%s'\n", ptr->next->name, ptr->next->value);
success = true; success = true;
} }
else { else
if (!SetVariable(pset->vars, options[0], options[1])) { {
if (!SetVariable(pset->vars, options[0], options[1]))
{
fprintf(stderr, "Set variable failed.\n"); fprintf(stderr, "Set variable failed.\n");
success = false; success = false;
} }
...@@ -510,27 +575,30 @@ exec_command(const char * cmd, ...@@ -510,27 +575,30 @@ exec_command(const char * cmd,
} }
/* \t -- turn off headers and row count */ /* \t -- turn off headers and row count */
else if (strcmp(cmd, "t")==0) else if (strcmp(cmd, "t") == 0)
success = do_pset("tuples_only", NULL, &pset->popt, quiet); success = do_pset("tuples_only", NULL, &pset->popt, quiet);
/* \T -- define html <table ...> attributes */ /* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T")==0) else if (strcmp(cmd, "T") == 0)
success = do_pset("tableattr", options[0], &pset->popt, quiet); success = do_pset("tableattr", options[0], &pset->popt, quiet);
/* \w -- write query buffer to file */ /* \w -- write query buffer to file */
else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0 ) else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0)
{ {
FILE *fd = NULL; FILE *fd = NULL;
bool pipe = false; bool pipe = false;
if (!options[0]) { if (!options[0])
{
fprintf(stderr, "Usage \\%s <filename>\n", cmd); fprintf(stderr, "Usage \\%s <filename>\n", cmd);
success = false; success = false;
} }
else { else
if (options[0][0] == '|') { {
if (options[0][0] == '|')
{
pipe = true; pipe = true;
#ifndef __CYGWIN32__ #ifndef __CYGWIN32__
fd = popen(&options[0][1], "w"); fd = popen(&options[0][1], "w");
...@@ -538,7 +606,8 @@ exec_command(const char * cmd, ...@@ -538,7 +606,8 @@ exec_command(const char * cmd,
fd = popen(&options[0][1], "wb"); fd = popen(&options[0][1], "wb");
#endif #endif
} }
else { else
{
#ifndef __CYGWIN32__ #ifndef __CYGWIN32__
fd = fopen(options[0], "w"); fd = fopen(options[0], "w");
#else #else
...@@ -546,13 +615,15 @@ exec_command(const char * cmd, ...@@ -546,13 +615,15 @@ exec_command(const char * cmd,
#endif #endif
} }
if (!fd) { if (!fd)
{
perror(options[0]); perror(options[0]);
success = false; success = false;
} }
} }
if (fd) { if (fd)
{
int result; int result;
if (query_buf && query_buf->len > 0) if (query_buf && query_buf->len > 0)
...@@ -563,7 +634,8 @@ exec_command(const char * cmd, ...@@ -563,7 +634,8 @@ exec_command(const char * cmd,
else else
result = fclose(fd); result = fclose(fd);
if (result == EOF) { if (result == EOF)
{
perror("close"); perror("close");
success = false; success = false;
} }
...@@ -571,38 +643,43 @@ exec_command(const char * cmd, ...@@ -571,38 +643,43 @@ exec_command(const char * cmd,
} }
/* \x -- toggle expanded table representation */ /* \x -- toggle expanded table representation */
else if (strcmp(cmd, "x")==0) else if (strcmp(cmd, "x") == 0)
success = do_pset("expanded", NULL, &pset->popt, quiet); success = do_pset("expanded", NULL, &pset->popt, quiet);
/* list table rights (grant/revoke) */ /* list table rights (grant/revoke) */
else if (strcmp(cmd, "z")==0) else if (strcmp(cmd, "z") == 0)
success = permissionsList(options[0], pset); success = permissionsList(options[0], pset);
else if (strcmp(cmd, "!")==0) else if (strcmp(cmd, "!") == 0)
success = do_shell(options_string); success = do_shell(options_string);
else if (strcmp(cmd, "?")==0) else if (strcmp(cmd, "?") == 0)
slashUsage(pset); slashUsage(pset);
#ifdef NOT_USED #ifdef NOT_USED
/* These commands don't do anything. I just use them to test the parser. */
else if (strcmp(cmd, "void")==0 || strcmp(cmd, "#")==0) /*
* These commands don't do anything. I just use them to test the
* parser.
*/
else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0)
{ {
int i; int i;
fprintf(stderr, "+ optline = |%s|\n", options_string); fprintf(stderr, "+ optline = |%s|\n", options_string);
for(i=0; options[i]; i++) for (i = 0; options[i]; i++)
fprintf(stderr, "+ opt%d = |%s|\n", i, options[i]); fprintf(stderr, "+ opt%d = |%s|\n", i, options[i]);
} }
#endif #endif
else { else
status = CMD_UNKNOWN; status = CMD_UNKNOWN;
}
if (!success) status = CMD_ERROR; if (!success)
status = CMD_ERROR;
return status; return status;
} }
...@@ -617,28 +694,32 @@ exec_command(const char * cmd, ...@@ -617,28 +694,32 @@ exec_command(const char * cmd,
* The return value is malloc()'ed. * The return value is malloc()'ed.
*/ */
static char * static char *
unescape(const char * source, PsqlSettings * pset) unescape(const char *source, PsqlSettings *pset)
{ {
unsigned char *p; unsigned char *p;
bool esc = false; /* Last character we saw was the bool esc = false; /* Last character we saw was the escape
escape character */ * character */
char *destination, *tmp; char *destination,
*tmp;
size_t length; size_t length;
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(source); assert(source);
#endif #endif
length = strlen(source)+1; length = strlen(source) + 1;
tmp = destination = (char *) malloc(length); tmp = destination = (char *) malloc(length);
if (!tmp) { if (!tmp)
{
perror("malloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (p = (char *) source; *p; p += PQmblen(p)) { for (p = (char *) source; *p; p += PQmblen(p))
if (esc) { {
if (esc)
{
char c; char c;
switch (*p) switch (*p)
...@@ -655,14 +736,23 @@ unescape(const char * source, PsqlSettings * pset) ...@@ -655,14 +736,23 @@ unescape(const char * source, PsqlSettings * pset)
case 'f': case 'f':
c = '\f'; c = '\f';
break; break;
case '0': case '1': case '2': case '3': case '4': case '0':
case '5': case '6': case '7': case '8': case '9': case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{ {
long int l; long int l;
char * end; char *end;
l = strtol(p, &end, 0); l = strtol(p, &end, 0);
c = l; c = l;
p = end-1; p = end - 1;
break; break;
} }
default: default:
...@@ -672,25 +762,27 @@ unescape(const char * source, PsqlSettings * pset) ...@@ -672,25 +762,27 @@ unescape(const char * source, PsqlSettings * pset)
esc = false; esc = false;
} }
else if (*p == '\\') { else if (*p == '\\')
esc = true; esc = true;
}
else if (*p == '$') else if (*p == '$')
{ {
if (*(p+1) == '{') { if (*(p + 1) == '{')
{
unsigned int len; unsigned int len;
char *copy; char *copy;
const char *value; const char *value;
void * new; void *new;
len = strcspn(p+2, "}");
copy = xstrdup(p+2); len = strcspn(p + 2, "}");
copy = xstrdup(p + 2);
copy[len] = '\0'; copy[len] = '\0';
value = interpolate_var(copy, pset); value = interpolate_var(copy, pset);
length += strlen(value) - (len+3); length += strlen(value) - (len + 3);
new = realloc(destination, length); new = realloc(destination, length);
if (!new) { if (!new)
{
perror("realloc"); perror("realloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -702,12 +794,12 @@ unescape(const char * source, PsqlSettings * pset) ...@@ -702,12 +794,12 @@ unescape(const char * source, PsqlSettings * pset)
p += len + 2; p += len + 2;
free(copy); free(copy);
} }
else { else
*tmp++ = '$'; *tmp++ = '$';
} }
}
else { else
{
*tmp++ = *p; *tmp++ = *p;
esc = false; esc = false;
} }
...@@ -731,62 +823,67 @@ unescape(const char * source, PsqlSettings * pset) ...@@ -731,62 +823,67 @@ unescape(const char * source, PsqlSettings * pset)
* but the old one was set back. Otherwise it terminates the program. * but the old one was set back. Otherwise it terminates the program.
*/ */
bool bool
do_connect(const char *new_dbname, const char *new_user, PsqlSettings * pset) do_connect(const char *new_dbname, const char *new_user, PsqlSettings *pset)
{ {
PGconn *oldconn = pset->db; PGconn *oldconn = pset->db;
const char *dbparam = NULL; const char *dbparam = NULL;
const char *userparam = NULL; const char *userparam = NULL;
char *pwparam = NULL; char *pwparam = NULL;
char * prompted_password = NULL; char *prompted_password = NULL;
char * prompted_user = NULL; char *prompted_user = NULL;
bool need_pass; bool need_pass;
bool success = false; bool success = false;
/* If dbname is "-" then use old name, else new one (even if NULL) */ /* If dbname is "-" then use old name, else new one (even if NULL) */
if (new_dbname && PQdb(oldconn) && (strcmp(new_dbname, "-") == 0 || strcmp(new_dbname, PQdb(oldconn))==0)) if (new_dbname && PQdb(oldconn) && (strcmp(new_dbname, "-") == 0 || strcmp(new_dbname, PQdb(oldconn)) == 0))
dbparam = PQdb(oldconn); dbparam = PQdb(oldconn);
else else
dbparam = new_dbname; dbparam = new_dbname;
/* If user is "" or "-" then use the old one */ /* If user is "" or "-" then use the old one */
if ( new_user && PQuser(oldconn) && ( strcmp(new_user, "")==0 || strcmp(new_user, "-")==0 || strcmp(new_user, PQuser(oldconn))==0 )) { if (new_user && PQuser(oldconn) && (strcmp(new_user, "") == 0 || strcmp(new_user, "-") == 0 || strcmp(new_user, PQuser(oldconn)) == 0))
userparam = PQuser(oldconn); userparam = PQuser(oldconn);
}
/* If username is "?" then prompt */ /* If username is "?" then prompt */
else if (new_user && strcmp(new_user, "?")==0) else if (new_user && strcmp(new_user, "?") == 0)
userparam = prompted_user = simple_prompt("Username: ", 100, true); /* save for free() */ userparam = prompted_user = simple_prompt("Username: ", 100, true); /* save for free() */
else else
userparam = new_user; userparam = new_user;
/* need to prompt for password? */ /* need to prompt for password? */
if (pset->getPassword) if (pset->getPassword)
pwparam = prompted_password = simple_prompt("Password: ", 100, false); /* need to save for free() */ pwparam = prompted_password = simple_prompt("Password: ", 100, false); /* need to save for
* free() */
/* Use old password if no new one given (if you didn't have an old one, fine) */ /*
* Use old password if no new one given (if you didn't have an old
* one, fine)
*/
if (!pwparam) if (!pwparam)
pwparam = PQpass(oldconn); pwparam = PQpass(oldconn);
#ifdef MULTIBYTE #ifdef MULTIBYTE
/* /*
* PGCLIENTENCODING may be set by the previous connection. if a * PGCLIENTENCODING may be set by the previous connection. if a user
* user does not explicitly set PGCLIENTENCODING, we should * does not explicitly set PGCLIENTENCODING, we should discard
* discard PGCLIENTENCODING so that libpq could get the backend * PGCLIENTENCODING so that libpq could get the backend encoding as
* encoding as the default PGCLIENTENCODING value. -- 1998/12/12 * the default PGCLIENTENCODING value. -- 1998/12/12 Tatsuo Ishii
* Tatsuo Ishii
*/ */
if (!pset->has_client_encoding) if (!pset->has_client_encoding)
putenv("PGCLIENTENCODING="); putenv("PGCLIENTENCODING=");
#endif #endif
do { do
{
need_pass = false; need_pass = false;
pset->db = PQsetdbLogin(PQhost(oldconn), PQport(oldconn), pset->db = PQsetdbLogin(PQhost(oldconn), PQport(oldconn),
NULL, NULL, dbparam, userparam, pwparam); NULL, NULL, dbparam, userparam, pwparam);
if (PQstatus(pset->db)==CONNECTION_BAD && if (PQstatus(pset->db) == CONNECTION_BAD &&
strcmp(PQerrorMessage(pset->db), "fe_sendauth: no password supplied\n")==0) { strcmp(PQerrorMessage(pset->db), "fe_sendauth: no password supplied\n") == 0)
{
need_pass = true; need_pass = true;
free(prompted_password); free(prompted_password);
prompted_password = NULL; prompted_password = NULL;
...@@ -797,32 +894,38 @@ do_connect(const char *new_dbname, const char *new_user, PsqlSettings * pset) ...@@ -797,32 +894,38 @@ do_connect(const char *new_dbname, const char *new_user, PsqlSettings * pset)
free(prompted_password); free(prompted_password);
free(prompted_user); free(prompted_user);
/* If connection failed, try at least keep the old one. /*
That's probably more convenient than just kicking you out of the * If connection failed, try at least keep the old one. That's
program. */ * probably more convenient than just kicking you out of the program.
*/
if (!pset->db || PQstatus(pset->db) == CONNECTION_BAD) if (!pset->db || PQstatus(pset->db) == CONNECTION_BAD)
{ {
fprintf(stderr, "Could not establish database connection.\n%s", PQerrorMessage(pset->db)); fprintf(stderr, "Could not establish database connection.\n%s", PQerrorMessage(pset->db));
PQfinish(pset->db); PQfinish(pset->db);
if (!oldconn || !pset->cur_cmd_interactive) { /* we don't want unpredictable things to happen if (!oldconn || !pset->cur_cmd_interactive)
in scripting mode */ { /* we don't want unpredictable things to
* happen in scripting mode */
fputs("Terminating.\n", stderr); fputs("Terminating.\n", stderr);
if (oldconn) if (oldconn)
PQfinish(oldconn); PQfinish(oldconn);
pset->db = NULL; pset->db = NULL;
} }
else { else
{
fputs("Keeping old connection.\n", stderr); fputs("Keeping old connection.\n", stderr);
pset->db = oldconn; pset->db = oldconn;
} }
} }
else { else
if (!GetVariable(pset->vars, "quiet")) { {
if (!GetVariable(pset->vars, "quiet"))
{
if (userparam != new_user) /* no new user */ if (userparam != new_user) /* no new user */
printf("You are now connected to database %s.\n", dbparam); printf("You are now connected to database %s.\n", dbparam);
else if (dbparam != new_dbname) /* no new db */ else if (dbparam != new_dbname) /* no new db */
printf("You are now connected as new user %s.\n", new_user); printf("You are now connected as new user %s.\n", new_user);
else /* both new */ else
/* both new */
printf("You are now connected to database %s as user %s.\n", printf("You are now connected to database %s as user %s.\n",
PQdb(pset->db), PQuser(pset->db)); PQdb(pset->db), PQuser(pset->db));
} }
...@@ -855,7 +958,8 @@ editFile(const char *fname) ...@@ -855,7 +958,8 @@ editFile(const char *fname)
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(fname); assert(fname);
#else #else
if (!fname) return false; if (!fname)
return false;
#endif #endif
/* Find an editor to use */ /* Find an editor to use */
...@@ -876,7 +980,7 @@ editFile(const char *fname) ...@@ -876,7 +980,7 @@ editFile(const char *fname)
perror(sys); perror(sys);
free(sys); free(sys);
return result==0; return result == 0;
} }
...@@ -885,24 +989,29 @@ static bool ...@@ -885,24 +989,29 @@ static bool
do_edit(const char *filename_arg, PQExpBuffer query_buf) do_edit(const char *filename_arg, PQExpBuffer query_buf)
{ {
char fnametmp[64]; char fnametmp[64];
FILE * stream; FILE *stream;
const char *fname; const char *fname;
bool error = false; bool error = false;
#ifndef WIN32 #ifndef WIN32
struct stat before, after; struct stat before,
after;
#endif #endif
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(query_buf); assert(query_buf);
#else #else
if (!query_buf) return false; if (!query_buf)
return false;
#endif #endif
if (filename_arg) if (filename_arg)
fname = filename_arg; fname = filename_arg;
else { else
{
/* make a temp file to edit */ /* make a temp file to edit */
#ifndef WIN32 #ifndef WIN32
mode_t oldumask; mode_t oldumask;
...@@ -911,7 +1020,7 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf) ...@@ -911,7 +1020,7 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
#else #else
GetTempFileName(".", "psql", 0, fnametmp); GetTempFileName(".", "psql", 0, fnametmp);
#endif #endif
fname = (const char *)fnametmp; fname = (const char *) fnametmp;
#ifndef WIN32 #ifndef WIN32
oldumask = umask(0177); oldumask = umask(0177);
...@@ -921,18 +1030,23 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf) ...@@ -921,18 +1030,23 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
umask(oldumask); umask(oldumask);
#endif #endif
if (!stream) { if (!stream)
{
perror(fname); perror(fname);
error = true; error = true;
} }
else { else
{
unsigned int ql = query_buf->len; unsigned int ql = query_buf->len;
if (ql == 0 || query_buf->data[ql - 1] != '\n') {
if (ql == 0 || query_buf->data[ql - 1] != '\n')
{
appendPQExpBufferChar(query_buf, '\n'); appendPQExpBufferChar(query_buf, '\n');
ql++; ql++;
} }
if (fwrite(query_buf->data, 1, ql, stream) != ql) { if (fwrite(query_buf->data, 1, ql, stream) != ql)
{
perror(fname); perror(fname);
fclose(stream); fclose(stream);
remove(fname); remove(fname);
...@@ -944,7 +1058,8 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf) ...@@ -944,7 +1058,8 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
} }
#ifndef WIN32 #ifndef WIN32
if (!error && stat(fname, &before) != 0) { if (!error && stat(fname, &before) != 0)
{
perror(fname); perror(fname);
error = true; error = true;
} }
...@@ -955,29 +1070,36 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf) ...@@ -955,29 +1070,36 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
error = !editFile(fname); error = !editFile(fname);
#ifndef WIN32 #ifndef WIN32
if (!error && stat(fname, &after) !=0) { if (!error && stat(fname, &after) != 0)
{
perror(fname); perror(fname);
error = true; error = true;
} }
if (!error && before.st_mtime != after.st_mtime) { if (!error && before.st_mtime != after.st_mtime)
{
#else #else
if (!error) { if (!error)
{
#endif #endif
stream = fopen(fname, "r"); stream = fopen(fname, "r");
if (!stream) { if (!stream)
{
perror(fname); perror(fname);
error = true; error = true;
} }
else { else
{
/* read file back in */ /* read file back in */
char line[1024]; char line[1024];
size_t result; size_t result;
resetPQExpBuffer(query_buf); resetPQExpBuffer(query_buf);
do { do
{
result = fread(line, 1, 1024, stream); result = fread(line, 1, 1024, stream);
if (ferror(stream)) { if (ferror(stream))
{
perror(fname); perror(fname);
error = true; error = true;
break; break;
...@@ -1020,7 +1142,8 @@ process_file(const char *filename, PsqlSettings *pset) ...@@ -1020,7 +1142,8 @@ process_file(const char *filename, PsqlSettings *pset)
fd = fopen(filename, "r"); fd = fopen(filename, "r");
#endif #endif
if (!fd) { if (!fd)
{
perror(filename); perror(filename);
return false; return false;
} }
...@@ -1039,7 +1162,8 @@ process_file(const char *filename, PsqlSettings *pset) ...@@ -1039,7 +1162,8 @@ process_file(const char *filename, PsqlSettings *pset)
static const char * static const char *
_align2string(enum printFormat in) _align2string(enum printFormat in)
{ {
switch (in) { switch (in)
{
case PRINT_NOTHING: case PRINT_NOTHING:
return "nothing"; return "nothing";
break; break;
...@@ -1061,31 +1185,35 @@ _align2string(enum printFormat in) ...@@ -1061,31 +1185,35 @@ _align2string(enum printFormat in)
bool bool
do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet) do_pset(const char *param, const char *value, printQueryOpt * popt, bool quiet)
{ {
size_t vallen = 0; size_t vallen = 0;
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(param); assert(param);
#else #else
if (!param) return false; if (!param)
return false;
#endif #endif
if (value) if (value)
vallen = strlen(value); vallen = strlen(value);
/* set format */ /* set format */
if (strcmp(param, "format")==0) { if (strcmp(param, "format") == 0)
{
if (!value) if (!value)
; ;
else if (strncasecmp("unaligned", value, vallen)==0) else if (strncasecmp("unaligned", value, vallen) == 0)
popt->topt.format = PRINT_UNALIGNED; popt->topt.format = PRINT_UNALIGNED;
else if (strncasecmp("aligned", value, vallen)==0) else if (strncasecmp("aligned", value, vallen) == 0)
popt->topt.format = PRINT_ALIGNED; popt->topt.format = PRINT_ALIGNED;
else if (strncasecmp("html", value, vallen)==0) else if (strncasecmp("html", value, vallen) == 0)
popt->topt.format = PRINT_HTML; popt->topt.format = PRINT_HTML;
else if (strncasecmp("latex", value, vallen)==0) else if (strncasecmp("latex", value, vallen) == 0)
popt->topt.format = PRINT_LATEX; popt->topt.format = PRINT_LATEX;
else { else
{
fprintf(stderr, "Allowed formats are unaligned, aligned, html, latex.\n"); fprintf(stderr, "Allowed formats are unaligned, aligned, html, latex.\n");
return false; return false;
} }
...@@ -1095,7 +1223,8 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1095,7 +1223,8 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
/* set border style/width */ /* set border style/width */
else if (strcmp(param, "border")==0) { else if (strcmp(param, "border") == 0)
{
if (value) if (value)
popt->topt.border = atoi(value); popt->topt.border = atoi(value);
...@@ -1104,15 +1233,18 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1104,15 +1233,18 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
/* set expanded/vertical mode */ /* set expanded/vertical mode */
else if (strcmp(param, "x")==0 || strcmp(param, "expanded")==0 || strcmp(param, "vertical")==0) { else if (strcmp(param, "x") == 0 || strcmp(param, "expanded") == 0 || strcmp(param, "vertical") == 0)
{
popt->topt.expanded = !popt->topt.expanded; popt->topt.expanded = !popt->topt.expanded;
if (!quiet) if (!quiet)
printf("Expanded display is %s.\n", popt->topt.expanded ? "on" : "off"); printf("Expanded display is %s.\n", popt->topt.expanded ? "on" : "off");
} }
/* null display */ /* null display */
else if (strcmp(param, "null")==0) { else if (strcmp(param, "null") == 0)
if (value) { {
if (value)
{
free(popt->nullPrint); free(popt->nullPrint);
popt->nullPrint = xstrdup(value); popt->nullPrint = xstrdup(value);
} }
...@@ -1121,8 +1253,10 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1121,8 +1253,10 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
/* field separator for unaligned text */ /* field separator for unaligned text */
else if (strcmp(param, "fieldsep")==0) { else if (strcmp(param, "fieldsep") == 0)
if (value) { {
if (value)
{
free(popt->topt.fieldSep); free(popt->topt.fieldSep);
popt->topt.fieldSep = xstrdup(value); popt->topt.fieldSep = xstrdup(value);
} }
...@@ -1131,9 +1265,11 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1131,9 +1265,11 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
/* toggle between full and barebones format */ /* toggle between full and barebones format */
else if (strcmp(param, "t")==0 || strcmp(param, "tuples_only")==0) { else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
{
popt->topt.tuples_only = !popt->topt.tuples_only; popt->topt.tuples_only = !popt->topt.tuples_only;
if (!quiet) { if (!quiet)
{
if (popt->topt.tuples_only) if (popt->topt.tuples_only)
puts("Showing only tuples."); puts("Showing only tuples.");
else else
...@@ -1142,14 +1278,16 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1142,14 +1278,16 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
/* set title override */ /* set title override */
else if (strcmp(param, "title")==0) { else if (strcmp(param, "title") == 0)
{
free(popt->title); free(popt->title);
if (!value) if (!value)
popt->title = NULL; popt->title = NULL;
else else
popt->title = xstrdup(value); popt->title = xstrdup(value);
if (!quiet) { if (!quiet)
{
if (popt->title) if (popt->title)
printf("Title is \"%s\".\n", popt->title); printf("Title is \"%s\".\n", popt->title);
else else
...@@ -1158,14 +1296,16 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1158,14 +1296,16 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
/* set HTML table tag options */ /* set HTML table tag options */
else if (strcmp(param, "T")==0 || strcmp(param, "tableattr")==0) { else if (strcmp(param, "T") == 0 || strcmp(param, "tableattr") == 0)
{
free(popt->topt.tableAttr); free(popt->topt.tableAttr);
if (!value) if (!value)
popt->topt.tableAttr = NULL; popt->topt.tableAttr = NULL;
else else
popt->topt.tableAttr = xstrdup(value); popt->topt.tableAttr = xstrdup(value);
if (!quiet) { if (!quiet)
{
if (popt->topt.tableAttr) if (popt->topt.tableAttr)
printf("Table attribute is \"%s\".\n", popt->topt.tableAttr); printf("Table attribute is \"%s\".\n", popt->topt.tableAttr);
else else
...@@ -1174,9 +1314,11 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1174,9 +1314,11 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
/* toggle use of pager */ /* toggle use of pager */
else if (strcmp(param, "pager")==0) { else if (strcmp(param, "pager") == 0)
{
popt->topt.pager = !popt->topt.pager; popt->topt.pager = !popt->topt.pager;
if (!quiet) { if (!quiet)
{
if (popt->topt.pager) if (popt->topt.pager)
puts("Using pager is on."); puts("Using pager is on.");
else else
...@@ -1185,7 +1327,8 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet ...@@ -1185,7 +1327,8 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
} }
else { else
{
fprintf(stderr, "Unknown option: %s\n", param); fprintf(stderr, "Unknown option: %s\n", param);
return false; return false;
} }
...@@ -1202,7 +1345,8 @@ do_shell(const char *command) ...@@ -1202,7 +1345,8 @@ do_shell(const char *command)
{ {
int result; int result;
if (!command) { if (!command)
{
char *sys; char *sys;
char *shellName; char *shellName;
...@@ -1220,7 +1364,8 @@ do_shell(const char *command) ...@@ -1220,7 +1364,8 @@ do_shell(const char *command)
else else
result = system(command); result = system(command);
if (result==127 || result==-1) { if (result == 127 || result == -1)
{
perror("system"); perror("system");
return false; return false;
} }
......
...@@ -11,37 +11,34 @@ ...@@ -11,37 +11,34 @@
typedef enum _backslashResult { typedef enum _backslashResult
{
CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */ CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */
CMD_SEND, /* query complete; send off */ CMD_SEND, /* query complete; send off */
CMD_SKIP_LINE, /* keep building query */ CMD_SKIP_LINE, /* keep building query */
CMD_TERMINATE, /* quit program */ CMD_TERMINATE, /* quit program */
CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */ CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */
CMD_ERROR /* the execution of the backslash command resulted CMD_ERROR /* the execution of the backslash command
in an error */ * resulted in an error */
} backslashResult; } backslashResult;
backslashResult backslashResult HandleSlashCmds(PsqlSettings *pset,
HandleSlashCmds(PsqlSettings *pset,
const char *line, const char *line,
PQExpBuffer query_buf, PQExpBuffer query_buf,
const char ** end_of_cmd); const char **end_of_cmd);
bool bool do_connect(const char *new_dbname,
do_connect(const char *new_dbname,
const char *new_user, const char *new_user,
PsqlSettings *pset); PsqlSettings *pset);
bool bool process_file(const char *filename,
process_file(const char *filename,
PsqlSettings *pset); PsqlSettings *pset);
bool bool do_pset(const char *param,
do_pset(const char * param, const char *value,
const char * value,
printQueryOpt * popt, printQueryOpt * popt,
bool quiet); bool quiet);
......
...@@ -39,15 +39,19 @@ ...@@ -39,15 +39,19 @@
* "Safe" wrapper around strdup() * "Safe" wrapper around strdup()
* (Using this also avoids writing #ifdef HAVE_STRDUP in every file :) * (Using this also avoids writing #ifdef HAVE_STRDUP in every file :)
*/ */
char * xstrdup(const char * string) char *
xstrdup(const char *string)
{ {
char * tmp; char *tmp;
if (!string) {
if (!string)
{
fprintf(stderr, "xstrdup: Cannot duplicate null pointer.\n"); fprintf(stderr, "xstrdup: Cannot duplicate null pointer.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
tmp = strdup(string); tmp = strdup(string);
if (!tmp) { if (!tmp)
{
perror("strdup"); perror("strdup");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -72,7 +76,8 @@ setQFout(const char *fname, PsqlSettings *pset) ...@@ -72,7 +76,8 @@ setQFout(const char *fname, PsqlSettings *pset)
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(pset); assert(pset);
#else #else
if (!pset) return false; if (!pset)
return false;
#endif #endif
/* Close old file/pipe */ /* Close old file/pipe */
...@@ -85,14 +90,14 @@ setQFout(const char *fname, PsqlSettings *pset) ...@@ -85,14 +90,14 @@ setQFout(const char *fname, PsqlSettings *pset)
} }
/* If no filename, set stdout */ /* If no filename, set stdout */
if (!fname || fname[0]=='\0') if (!fname || fname[0] == '\0')
{ {
pset->queryFout = stdout; pset->queryFout = stdout;
pset->queryFoutPipe = false; pset->queryFoutPipe = false;
} }
else if (*fname == '|') else if (*fname == '|')
{ {
const char * pipename = fname+1; const char *pipename = fname + 1;
#ifndef __CYGWIN32__ #ifndef __CYGWIN32__
...@@ -147,16 +152,19 @@ char * ...@@ -147,16 +152,19 @@ char *
simple_prompt(const char *prompt, int maxlen, bool echo) simple_prompt(const char *prompt, int maxlen, bool echo)
{ {
int length; int length;
char * destination; char *destination;
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
struct termios t_orig, t; struct termios t_orig,
t;
#endif #endif
destination = (char *) malloc(maxlen+2); destination = (char *) malloc(maxlen + 2);
if (!destination) if (!destination)
return NULL; return NULL;
if (prompt) fputs(prompt, stdout); if (prompt)
fputs(prompt, stdout);
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
if (!echo) if (!echo)
...@@ -171,17 +179,21 @@ simple_prompt(const char *prompt, int maxlen, bool echo) ...@@ -171,17 +179,21 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
fgets(destination, maxlen, stdin); fgets(destination, maxlen, stdin);
#ifdef HAVE_TERMIOS_H #ifdef HAVE_TERMIOS_H
if (!echo) { if (!echo)
{
tcsetattr(0, TCSADRAIN, &t_orig); tcsetattr(0, TCSADRAIN, &t_orig);
puts(""); puts("");
} }
#endif #endif
length = strlen(destination); length = strlen(destination);
if (length > 0 && destination[length - 1] != '\n') { if (length > 0 && destination[length - 1] != '\n')
{
/* eat rest of the line */ /* eat rest of the line */
char buf[512]; char buf[512];
do {
do
{
fgets(buf, 512, stdin); fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n'); } while (buf[strlen(buf) - 1] != '\n');
} }
...@@ -205,18 +217,20 @@ simple_prompt(const char *prompt, int maxlen, bool echo) ...@@ -205,18 +217,20 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
* immediate consumption. * immediate consumption.
*/ */
const char * const char *
interpolate_var(const char * name, PsqlSettings * pset) interpolate_var(const char *name, PsqlSettings *pset)
{ {
const char * var; const char *var;
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(name); assert(name);
assert(pset); assert(pset);
#else #else
if (!name || !pset) return NULL; if (!name || !pset)
return NULL;
#endif #endif
if (strspn(name, VALID_VARIABLE_CHARS) == strlen(name)) { if (strspn(name, VALID_VARIABLE_CHARS) == strlen(name))
{
var = GetVariable(pset->vars, name); var = GetVariable(pset->vars, name);
if (var) if (var)
return var; return var;
...@@ -225,41 +239,50 @@ interpolate_var(const char * name, PsqlSettings * pset) ...@@ -225,41 +239,50 @@ interpolate_var(const char * name, PsqlSettings * pset)
} }
/* otherwise return magic variable */ /* otherwise return magic variable */
/* (by convention these should be capitalized (but not all caps), to not be
shadowed by regular vars or to shadow env vars) */ /*
if (strcmp(name, "Version")==0) * (by convention these should be capitalized (but not all caps), to
* not be shadowed by regular vars or to shadow env vars)
*/
if (strcmp(name, "Version") == 0)
return PG_VERSION_STR; return PG_VERSION_STR;
if (strcmp(name, "Database")==0) { if (strcmp(name, "Database") == 0)
{
if (PQdb(pset->db)) if (PQdb(pset->db))
return PQdb(pset->db); return PQdb(pset->db);
else else
return ""; return "";
} }
if (strcmp(name, "User")==0) { if (strcmp(name, "User") == 0)
{
if (PQuser(pset->db)) if (PQuser(pset->db))
return PQuser(pset->db); return PQuser(pset->db);
else else
return ""; return "";
} }
if (strcmp(name, "Host")==0) { if (strcmp(name, "Host") == 0)
{
if (PQhost(pset->db)) if (PQhost(pset->db))
return PQhost(pset->db); return PQhost(pset->db);
else else
return ""; return "";
} }
if (strcmp(name, "Port")==0) { if (strcmp(name, "Port") == 0)
{
if (PQport(pset->db)) if (PQport(pset->db))
return PQport(pset->db); return PQport(pset->db);
else else
return ""; return "";
} }
/* env vars (if env vars are all caps there should be no prob, otherwise /*
you're on your own */ * env vars (if env vars are all caps there should be no prob,
* otherwise you're on your own
*/
if ((var = getenv(name))) if ((var = getenv(name)))
return var; return var;
...@@ -284,7 +307,7 @@ interpolate_var(const char * name, PsqlSettings * pset) ...@@ -284,7 +307,7 @@ interpolate_var(const char * name, PsqlSettings * pset)
* at least avoid trusting printf by using the more primitive fputs(). * at least avoid trusting printf by using the more primitive fputs().
*/ */
PGconn * cancelConn; PGconn *cancelConn;
#ifdef WIN32 #ifdef WIN32
#define safe_write_stderr(String) fputs(s, stderr) #define safe_write_stderr(String) fputs(s, stderr)
...@@ -302,7 +325,8 @@ handle_sigint(SIGNAL_ARGS) ...@@ -302,7 +325,8 @@ handle_sigint(SIGNAL_ARGS)
/* Try to send cancel request */ /* Try to send cancel request */
if (PQrequestCancel(cancelConn)) if (PQrequestCancel(cancelConn))
safe_write_stderr("\nCANCEL request sent\n"); safe_write_stderr("\nCANCEL request sent\n");
else { else
{
safe_write_stderr("\nCould not send cancel request: "); safe_write_stderr("\nCould not send cancel request: ");
safe_write_stderr(PQerrorMessage(cancelConn)); safe_write_stderr(PQerrorMessage(cancelConn));
} }
...@@ -320,20 +344,22 @@ PGresult * ...@@ -320,20 +344,22 @@ PGresult *
PSQLexec(PsqlSettings *pset, const char *query) PSQLexec(PsqlSettings *pset, const char *query)
{ {
PGresult *res; PGresult *res;
const char * var; const char *var;
if (!pset->db) { if (!pset->db)
{
fputs("You are not currently connected to a database.\n", stderr); fputs("You are not currently connected to a database.\n", stderr);
return NULL; return NULL;
} }
var = GetVariable(pset->vars, "echo_secret"); var = GetVariable(pset->vars, "echo_secret");
if (var) { if (var)
{
printf("********* QUERY *********\n%s\n*************************\n\n", query); printf("********* QUERY *********\n%s\n*************************\n\n", query);
fflush(stdout); fflush(stdout);
} }
if (var && strcmp(var, "noexec")==0) if (var && strcmp(var, "noexec") == 0)
return NULL; return NULL;
cancelConn = pset->db; cancelConn = pset->db;
...@@ -347,7 +373,8 @@ PSQLexec(PsqlSettings *pset, const char *query) ...@@ -347,7 +373,8 @@ PSQLexec(PsqlSettings *pset, const char *query)
{ {
fputs("The connection to the server was lost. Attempting reset: ", stderr); fputs("The connection to the server was lost. Attempting reset: ", stderr);
PQreset(pset->db); PQreset(pset->db);
if (PQstatus(pset->db) == CONNECTION_BAD) { if (PQstatus(pset->db) == CONNECTION_BAD)
{
fputs("Failed.\n", stderr); fputs("Failed.\n", stderr);
PQfinish(pset->db); PQfinish(pset->db);
PQclear(res); PQclear(res);
...@@ -364,7 +391,8 @@ PSQLexec(PsqlSettings *pset, const char *query) ...@@ -364,7 +391,8 @@ PSQLexec(PsqlSettings *pset, const char *query)
PQresultStatus(res) == PGRES_COPY_OUT) PQresultStatus(res) == PGRES_COPY_OUT)
) )
return res; return res;
else { else
{
fprintf(stderr, "%s", PQerrorMessage(pset->db)); fprintf(stderr, "%s", PQerrorMessage(pset->db));
PQclear(res); PQclear(res);
return NULL; return NULL;
...@@ -392,20 +420,23 @@ SendQuery(PsqlSettings *pset, const char *query) ...@@ -392,20 +420,23 @@ SendQuery(PsqlSettings *pset, const char *query)
PGresult *results; PGresult *results;
PGnotify *notify; PGnotify *notify;
if (!pset->db) { if (!pset->db)
{
fputs("You are not currently connected to a database.\n", stderr); fputs("You are not currently connected to a database.\n", stderr);
return false; return false;
} }
if (GetVariableBool(pset->vars, "singlestep")) { if (GetVariableBool(pset->vars, "singlestep"))
{
char buf[3]; char buf[3];
fprintf(stdout, "***(Single step mode: Verify query)*********************************************\n" fprintf(stdout, "***(Single step mode: Verify query)*********************************************\n"
"QUERY: %s\n" "QUERY: %s\n"
"***(press return to proceed or enter x and return to cancel)********************\n", "***(press return to proceed or enter x and return to cancel)********************\n",
query); query);
fflush(stdout); fflush(stdout);
fgets(buf, 3, stdin); fgets(buf, 3, stdin);
if (buf[0]=='x') if (buf[0] == 'x')
return false; return false;
fflush(stdin); fflush(stdin);
} }
...@@ -432,7 +463,8 @@ SendQuery(PsqlSettings *pset, const char *query) ...@@ -432,7 +463,8 @@ SendQuery(PsqlSettings *pset, const char *query)
PsqlSettings settings_copy = *pset; PsqlSettings settings_copy = *pset;
settings_copy.queryFout = stdout; settings_copy.queryFout = stdout;
if (!setQFout(pset->gfname, &settings_copy)) { if (!setQFout(pset->gfname, &settings_copy))
{
success = false; success = false;
break; break;
} }
...@@ -491,7 +523,8 @@ SendQuery(PsqlSettings *pset, const char *query) ...@@ -491,7 +523,8 @@ SendQuery(PsqlSettings *pset, const char *query)
{ {
fputs("The connection to the server was lost. Attempting reset: ", stderr); fputs("The connection to the server was lost. Attempting reset: ", stderr);
PQreset(pset->db); PQreset(pset->db);
if (PQstatus(pset->db) == CONNECTION_BAD) { if (PQstatus(pset->db) == CONNECTION_BAD)
{
fputs("Failed.\n", stderr); fputs("Failed.\n", stderr);
PQfinish(pset->db); PQfinish(pset->db);
PQclear(results); PQclear(results);
......
...@@ -5,21 +5,21 @@ ...@@ -5,21 +5,21 @@
#include "settings.h" #include "settings.h"
char * char *
xstrdup(const char * string); xstrdup(const char *string);
bool bool
setQFout(const char *fname, PsqlSettings *pset); setQFout(const char *fname, PsqlSettings *pset);
char * char *
simple_prompt(const char *prompt, int maxlen, bool echo); simple_prompt(const char *prompt, int maxlen, bool echo);
const char * const char *
interpolate_var(const char * name, PsqlSettings * pset); interpolate_var(const char *name, PsqlSettings *pset);
PGresult * PGresult *
PSQLexec(PsqlSettings *pset, const char *query); PSQLexec(PsqlSettings *pset, const char *query);
bool bool
SendQuery(PsqlSettings *pset, const char *query); SendQuery(PsqlSettings *pset, const char *query);
#endif /* COMMON_H */ #endif /* COMMON_H */
...@@ -33,13 +33,14 @@ ...@@ -33,13 +33,14 @@
* returns a malloc'ed structure with the options, or NULL on parsing error * returns a malloc'ed structure with the options, or NULL on parsing error
*/ */
struct copy_options { struct copy_options
char * table; {
char * file; char *table;
char *file;
bool from; bool from;
bool binary; bool binary;
bool oids; bool oids;
char * delim; char *delim;
}; };
...@@ -58,15 +59,16 @@ free_copy_options(struct copy_options * ptr) ...@@ -58,15 +59,16 @@ free_copy_options(struct copy_options * ptr)
static struct copy_options * static struct copy_options *
parse_slash_copy(const char *args) parse_slash_copy(const char *args)
{ {
struct copy_options * result; struct copy_options *result;
char * line; char *line;
char * token; char *token;
bool error = false; bool error = false;
char quote; char quote;
line = xstrdup(args); line = xstrdup(args);
if (!(result = calloc(1, sizeof (struct copy_options)))) { if (!(result = calloc(1, sizeof(struct copy_options))))
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -74,8 +76,10 @@ parse_slash_copy(const char *args) ...@@ -74,8 +76,10 @@ parse_slash_copy(const char *args)
token = strtokx(line, " \t", "\"", '\\', &quote, NULL); token = strtokx(line, " \t", "\"", '\\', &quote, NULL);
if (!token) if (!token)
error = true; error = true;
else { else
if (!quote && strcasecmp(token, "binary")==0) { {
if (!quote && strcasecmp(token, "binary") == 0)
{
result->binary = true; result->binary = true;
token = strtokx(NULL, " \t", "\"", '\\', &quote, NULL); token = strtokx(NULL, " \t", "\"", '\\', &quote, NULL);
if (!token) if (!token)
...@@ -89,56 +93,65 @@ parse_slash_copy(const char *args) ...@@ -89,56 +93,65 @@ parse_slash_copy(const char *args)
assert(error || result->table); assert(error || result->table);
#endif #endif
if (!error) { if (!error)
{
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token) if (!token)
error = true; error = true;
else { else
if (strcasecmp(token, "with")==0) { {
if (strcasecmp(token, "with") == 0)
{
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token || strcasecmp(token, "oids")!=0) if (!token || strcasecmp(token, "oids") != 0)
error = true; error = true;
else else
result->oids = true; result->oids = true;
if (!error) { if (!error)
{
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token) if (!token)
error = true; error = true;
} }
} }
if (!error && strcasecmp(token, "from")==0) if (!error && strcasecmp(token, "from") == 0)
result->from = true; result->from = true;
else if (!error && strcasecmp(token, "to")==0) else if (!error && strcasecmp(token, "to") == 0)
result->from = false; result->from = false;
else else
error = true; error = true;
} }
} }
if (!error) { if (!error)
{
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL); token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
if (!token) if (!token)
error = true; error = true;
else else
result->file=xstrdup(token); result->file = xstrdup(token);
} }
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(error || result->file); assert(error || result->file);
#endif #endif
if (!error) { if (!error)
{
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (token) { if (token)
if (strcasecmp(token, "using")!=0) {
if (strcasecmp(token, "using") != 0)
error = true; error = true;
else { else
{
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token || strcasecmp(token, "delimiters")!=0) if (!token || strcasecmp(token, "delimiters") != 0)
error = true; error = true;
else { else
{
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL); token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
if (token) if (token)
result->delim = xstrdup(token); result->delim = xstrdup(token);
...@@ -151,7 +164,8 @@ parse_slash_copy(const char *args) ...@@ -151,7 +164,8 @@ parse_slash_copy(const char *args)
free(line); free(line);
if (error) { if (error)
{
fputs("Parse error at ", stderr); fputs("Parse error at ", stderr);
if (!token) if (!token)
fputs("end of line.", stderr); fputs("end of line.", stderr);
...@@ -173,7 +187,7 @@ parse_slash_copy(const char *args) ...@@ -173,7 +187,7 @@ parse_slash_copy(const char *args)
* file or route its response into the file. * file or route its response into the file.
*/ */
bool bool
do_copy(const char * args, PsqlSettings *pset) do_copy(const char *args, PsqlSettings *pset)
{ {
char query[128 + NAMEDATALEN]; char query[128 + NAMEDATALEN];
FILE *copystream; FILE *copystream;
...@@ -204,10 +218,14 @@ do_copy(const char * args, PsqlSettings *pset) ...@@ -204,10 +218,14 @@ do_copy(const char * args, PsqlSettings *pset)
strcat(query, "TO stdout"); strcat(query, "TO stdout");
if (options->delim) { if (options->delim)
/* backend copy only uses the first character here, {
but that might be the escape backslash
(makes me wonder though why it's called delimiterS) */ /*
* backend copy only uses the first character here, but that might
* be the escape backslash (makes me wonder though why it's called
* delimiterS)
*/
strncat(query, " USING DELIMITERS '", 2); strncat(query, " USING DELIMITERS '", 2);
strcat(query, options->delim); strcat(query, options->delim);
strcat(query, "'"); strcat(query, "'");
...@@ -227,7 +245,8 @@ do_copy(const char * args, PsqlSettings *pset) ...@@ -227,7 +245,8 @@ do_copy(const char * args, PsqlSettings *pset)
copystream = fopen(options->file, "wb"); copystream = fopen(options->file, "wb");
#endif #endif
if (!copystream) { if (!copystream)
{
fprintf(stderr, fprintf(stderr,
"Unable to open file %s which to copy: %s\n", "Unable to open file %s which to copy: %s\n",
options->from ? "from" : "to", strerror(errno)); options->from ? "from" : "to", strerror(errno));
...@@ -258,7 +277,8 @@ do_copy(const char * args, PsqlSettings *pset) ...@@ -258,7 +277,8 @@ do_copy(const char * args, PsqlSettings *pset)
PQclear(result); PQclear(result);
if (!GetVariable(pset->vars, "quiet")) { if (!GetVariable(pset->vars, "quiet"))
{
if (success) if (success)
puts("Successfully copied."); puts("Successfully copied.");
else else
...@@ -337,7 +357,7 @@ handleCopyOut(PGconn *conn, FILE *copystream) ...@@ -337,7 +357,7 @@ handleCopyOut(PGconn *conn, FILE *copystream)
*/ */
bool bool
handleCopyIn(PGconn *conn, FILE *copystream, const char * prompt) handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt)
{ {
bool copydone = false; bool copydone = false;
bool firstload; bool firstload;
......
...@@ -8,15 +8,15 @@ ...@@ -8,15 +8,15 @@
/* handler for \copy */ /* handler for \copy */
bool bool
do_copy(const char *args, PsqlSettings *pset); do_copy(const char *args, PsqlSettings *pset);
/* lower level processors for copy in/out streams */ /* lower level processors for copy in/out streams */
bool bool
handleCopyOut(PGconn *conn, FILE *copystream); handleCopyOut(PGconn *conn, FILE *copystream);
bool bool
handleCopyIn(PGconn *conn, FILE *copystream, const char * prompt); handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt);
#endif #endif
...@@ -34,17 +34,21 @@ ...@@ -34,17 +34,21 @@
* takes an optional regexp to match specific aggregates by name * takes an optional regexp to match specific aggregates by name
*/ */
bool bool
describeAggregates(const char * name, PsqlSettings * pset) describeAggregates(const char *name, PsqlSettings *pset)
{ {
char descbuf[384 + 2*REGEXP_CUTOFF]; /* observe/adjust this if you change the query */ char descbuf[384 + 2 * REGEXP_CUTOFF]; /* observe/adjust this
PGresult * res; * if you change the
* query */
PGresult *res;
bool description = GetVariableBool(pset->vars, "description"); bool description = GetVariableBool(pset->vars, "description");
printQueryOpt myopt = pset->popt; printQueryOpt myopt = pset->popt;
descbuf[0] = '\0'; descbuf[0] = '\0';
/* There are two kinds of aggregates: ones that work on particular types /*
ones that work on all */ * There are two kinds of aggregates: ones that work on particular
* types ones that work on all
*/
strcat(descbuf, strcat(descbuf,
"SELECT a.aggname AS \"Name\", t.typname AS \"Type\""); "SELECT a.aggname AS \"Name\", t.typname AS \"Type\"");
if (description) if (description)
...@@ -53,7 +57,8 @@ describeAggregates(const char * name, PsqlSettings * pset) ...@@ -53,7 +57,8 @@ describeAggregates(const char * name, PsqlSettings * pset)
strcat(descbuf, strcat(descbuf,
"\nFROM pg_aggregate a, pg_type t\n" "\nFROM pg_aggregate a, pg_type t\n"
"WHERE a.aggbasetype = t.oid\n"); "WHERE a.aggbasetype = t.oid\n");
if (name) { if (name)
{
strcat(descbuf, " AND a.aggname ~* '^"); strcat(descbuf, " AND a.aggname ~* '^");
strncat(descbuf, name, REGEXP_CUTOFF); strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "'\n"); strcat(descbuf, "'\n");
...@@ -96,16 +101,15 @@ describeAggregates(const char * name, PsqlSettings * pset) ...@@ -96,16 +101,15 @@ describeAggregates(const char * name, PsqlSettings * pset)
* takes an optional regexp to narrow down the function name * takes an optional regexp to narrow down the function name
*/ */
bool bool
describeFunctions(const char * name, PsqlSettings * pset) describeFunctions(const char *name, PsqlSettings *pset)
{ {
char descbuf[384 + REGEXP_CUTOFF]; char descbuf[384 + REGEXP_CUTOFF];
PGresult * res; PGresult *res;
printQueryOpt myopt = pset->popt; printQueryOpt myopt = pset->popt;
/* /*
* we skip in/out funcs by excluding functions that take * we skip in/out funcs by excluding functions that take some
* some arguments, but have no types defined for those * arguments, but have no types defined for those arguments
* arguments
*/ */
descbuf[0] = '\0'; descbuf[0] = '\0';
...@@ -145,10 +149,10 @@ describeFunctions(const char * name, PsqlSettings * pset) ...@@ -145,10 +149,10 @@ describeFunctions(const char * name, PsqlSettings * pset)
* for \dT * for \dT
*/ */
bool bool
describeTypes(const char * name, PsqlSettings * pset) describeTypes(const char *name, PsqlSettings *pset)
{ {
char descbuf[256 + REGEXP_CUTOFF]; char descbuf[256 + REGEXP_CUTOFF];
PGresult * res; PGresult *res;
printQueryOpt myopt = pset->popt; printQueryOpt myopt = pset->popt;
descbuf[0] = '\0'; descbuf[0] = '\0';
...@@ -158,7 +162,8 @@ describeTypes(const char * name, PsqlSettings * pset) ...@@ -158,7 +162,8 @@ describeTypes(const char * name, PsqlSettings * pset)
strcat(descbuf, "\nFROM pg_type\n" strcat(descbuf, "\nFROM pg_type\n"
"WHERE typrelid = 0 AND typname !~ '^_.*'\n"); "WHERE typrelid = 0 AND typname !~ '^_.*'\n");
if (name) { if (name)
{
strcat(descbuf, " AND typname ~* '^"); strcat(descbuf, " AND typname ~* '^");
strncat(descbuf, name, REGEXP_CUTOFF); strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "' "); strcat(descbuf, "' ");
...@@ -187,10 +192,11 @@ describeTypes(const char * name, PsqlSettings * pset) ...@@ -187,10 +192,11 @@ describeTypes(const char * name, PsqlSettings * pset)
* exact match string. * exact match string.
*/ */
bool bool
describeOperators(const char * name, PsqlSettings * pset) describeOperators(const char *name, PsqlSettings *pset)
{ {
char descbuf[1536 + 3 * 32]; /* 32 is max length for operator name */ char descbuf[1536 + 3 * 32]; /* 32 is max length for operator
PGresult * res; * name */
PGresult *res;
bool description = GetVariableBool(pset->vars, "description"); bool description = GetVariableBool(pset->vars, "description");
printQueryOpt myopt = pset->popt; printQueryOpt myopt = pset->popt;
...@@ -316,7 +322,7 @@ listAllDbs(PsqlSettings *pset) ...@@ -316,7 +322,7 @@ listAllDbs(PsqlSettings *pset)
* *
*/ */
bool bool
permissionsList(const char * name, PsqlSettings *pset) permissionsList(const char *name, PsqlSettings *pset)
{ {
char descbuf[256 + REGEXP_CUTOFF]; char descbuf[256 + REGEXP_CUTOFF];
PGresult *res; PGresult *res;
...@@ -329,21 +335,22 @@ permissionsList(const char * name, PsqlSettings *pset) ...@@ -329,21 +335,22 @@ permissionsList(const char * name, PsqlSettings *pset)
"FROM pg_class\n" "FROM pg_class\n"
"WHERE ( relkind = 'r' OR relkind = 'S') AND\n" "WHERE ( relkind = 'r' OR relkind = 'S') AND\n"
" relname !~ '^pg_'\n"); " relname !~ '^pg_'\n");
if (name) { if (name)
{
strcat(descbuf, " AND rename ~ '^"); strcat(descbuf, " AND rename ~ '^");
strncat(descbuf, name, REGEXP_CUTOFF); strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "'\n"); strcat(descbuf, "'\n");
} }
strcat (descbuf, "ORDER BY relname"); strcat(descbuf, "ORDER BY relname");
res = PSQLexec(pset, descbuf); res = PSQLexec(pset, descbuf);
if (!res) if (!res)
return false; return false;
if (PQntuples(res) == 0) { if (PQntuples(res) == 0)
fputs("Couldn't find any tables.\n", pset->queryFout); fputs("Couldn't find any tables.\n", pset->queryFout);
} else
else { {
myopt.topt.tuples_only = false; myopt.topt.tuples_only = false;
myopt.nullPrint = NULL; myopt.nullPrint = NULL;
sprintf(descbuf, "Access permissions for database \"%s\"", PQdb(pset->db)); sprintf(descbuf, "Access permissions for database \"%s\"", PQdb(pset->db));
...@@ -368,9 +375,9 @@ permissionsList(const char * name, PsqlSettings *pset) ...@@ -368,9 +375,9 @@ permissionsList(const char * name, PsqlSettings *pset)
* lists of things, there are other \d? commands. * lists of things, there are other \d? commands.
*/ */
bool bool
objectDescription(const char * object, PsqlSettings *pset) objectDescription(const char *object, PsqlSettings *pset)
{ {
char descbuf[2048 + 7*REGEXP_CUTOFF]; char descbuf[2048 + 7 * REGEXP_CUTOFF];
PGresult *res; PGresult *res;
printQueryOpt myopt = pset->popt; printQueryOpt myopt = pset->popt;
...@@ -380,10 +387,11 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -380,10 +387,11 @@ objectDescription(const char * object, PsqlSettings *pset)
strcat(descbuf, "SELECT DISTINCT a.aggname as \"Name\", 'aggregate'::text as \"What\", d.description as \"Description\"\n" strcat(descbuf, "SELECT DISTINCT a.aggname as \"Name\", 'aggregate'::text as \"What\", d.description as \"Description\"\n"
"FROM pg_aggregate a, pg_description d\n" "FROM pg_aggregate a, pg_description d\n"
"WHERE a.oid = d.objoid\n"); "WHERE a.oid = d.objoid\n");
if (object) { if (object)
strcat(descbuf," AND a.aggname ~* '^"); {
strcat(descbuf, " AND a.aggname ~* '^");
strncat(descbuf, object, REGEXP_CUTOFF); strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf,"'\n"); strcat(descbuf, "'\n");
} }
/* Function descriptions (except in/outs for datatypes) */ /* Function descriptions (except in/outs for datatypes) */
...@@ -391,10 +399,11 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -391,10 +399,11 @@ objectDescription(const char * object, PsqlSettings *pset)
strcat(descbuf, "SELECT DISTINCT p.proname as \"Name\", 'function'::text as \"What\", d.description as \"Description\"\n" strcat(descbuf, "SELECT DISTINCT p.proname as \"Name\", 'function'::text as \"What\", d.description as \"Description\"\n"
"FROM pg_proc p, pg_description d\n" "FROM pg_proc p, pg_description d\n"
"WHERE p.oid = d.objoid AND (p.pronargs = 0 or oid8types(p.proargtypes) != '')\n"); "WHERE p.oid = d.objoid AND (p.pronargs = 0 or oid8types(p.proargtypes) != '')\n");
if (object) { if (object)
strcat(descbuf," AND p.proname ~* '^"); {
strcat(descbuf, " AND p.proname ~* '^");
strncat(descbuf, object, REGEXP_CUTOFF); strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf,"'\n"); strcat(descbuf, "'\n");
} }
/* Operator descriptions */ /* Operator descriptions */
...@@ -403,10 +412,11 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -403,10 +412,11 @@ objectDescription(const char * object, PsqlSettings *pset)
"FROM pg_operator o, pg_description d\n" "FROM pg_operator o, pg_description d\n"
// must get comment via associated function // must get comment via associated function
"WHERE RegprocToOid(o.oprcode) = d.objoid\n"); "WHERE RegprocToOid(o.oprcode) = d.objoid\n");
if (object) { if (object)
strcat(descbuf," AND o.oprname = '"); {
strcat(descbuf, " AND o.oprname = '");
strncat(descbuf, object, REGEXP_CUTOFF); strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf,"'\n"); strcat(descbuf, "'\n");
} }
/* Type description */ /* Type description */
...@@ -414,10 +424,11 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -414,10 +424,11 @@ objectDescription(const char * object, PsqlSettings *pset)
strcat(descbuf, "SELECT DISTINCT t.typname as \"Name\", 'type'::text as \"What\", d.description as \"Description\"\n" strcat(descbuf, "SELECT DISTINCT t.typname as \"Name\", 'type'::text as \"What\", d.description as \"Description\"\n"
"FROM pg_type t, pg_description d\n" "FROM pg_type t, pg_description d\n"
"WHERE t.oid = d.objoid\n"); "WHERE t.oid = d.objoid\n");
if (object) { if (object)
strcat(descbuf," AND t.typname ~* '^"); {
strcat(descbuf, " AND t.typname ~* '^");
strncat(descbuf, object, REGEXP_CUTOFF); strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf,"'\n"); strcat(descbuf, "'\n");
} }
/* Relation (tables, views, indices, sequences) descriptions */ /* Relation (tables, views, indices, sequences) descriptions */
...@@ -425,10 +436,11 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -425,10 +436,11 @@ objectDescription(const char * object, PsqlSettings *pset)
strcat(descbuf, "SELECT DISTINCT c.relname as \"Name\", 'relation'::text||'('||c.relkind||')' as \"What\", d.description as \"Description\"\n" strcat(descbuf, "SELECT DISTINCT c.relname as \"Name\", 'relation'::text||'('||c.relkind||')' as \"What\", d.description as \"Description\"\n"
"FROM pg_class c, pg_description d\n" "FROM pg_class c, pg_description d\n"
"WHERE c.oid = d.objoid\n"); "WHERE c.oid = d.objoid\n");
if (object) { if (object)
strcat(descbuf," AND c.relname ~* '^"); {
strcat(descbuf, " AND c.relname ~* '^");
strncat(descbuf, object, REGEXP_CUTOFF); strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf,"'\n"); strcat(descbuf, "'\n");
} }
/* Rule description (ignore rules for views) */ /* Rule description (ignore rules for views) */
...@@ -436,10 +448,11 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -436,10 +448,11 @@ objectDescription(const char * object, PsqlSettings *pset)
strcat(descbuf, "SELECT DISTINCT r.rulename as \"Name\", 'rule'::text as \"What\", d.description as \"Description\"\n" strcat(descbuf, "SELECT DISTINCT r.rulename as \"Name\", 'rule'::text as \"What\", d.description as \"Description\"\n"
"FROM pg_rewrite r, pg_description d\n" "FROM pg_rewrite r, pg_description d\n"
"WHERE r.oid = d.objoid AND r.rulename !~ '^_RET'\n"); "WHERE r.oid = d.objoid AND r.rulename !~ '^_RET'\n");
if (object) { if (object)
strcat(descbuf," AND r.rulename ~* '^"); {
strcat(descbuf, " AND r.rulename ~* '^");
strncat(descbuf, object, REGEXP_CUTOFF); strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf,"'\n"); strcat(descbuf, "'\n");
} }
/* Trigger description */ /* Trigger description */
...@@ -447,10 +460,11 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -447,10 +460,11 @@ objectDescription(const char * object, PsqlSettings *pset)
strcat(descbuf, "SELECT DISTINCT t.tgname as \"Name\", 'trigger'::text as \"What\", d.description as \"Description\"\n" strcat(descbuf, "SELECT DISTINCT t.tgname as \"Name\", 'trigger'::text as \"What\", d.description as \"Description\"\n"
"FROM pg_trigger t, pg_description d\n" "FROM pg_trigger t, pg_description d\n"
"WHERE t.oid = d.objoid\n"); "WHERE t.oid = d.objoid\n");
if (object) { if (object)
strcat(descbuf," AND t.tgname ~* '^"); {
strcat(descbuf, " AND t.tgname ~* '^");
strncat(descbuf, object, REGEXP_CUTOFF); strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf,"'\n"); strcat(descbuf, "'\n");
} }
strcat(descbuf, "\nORDER BY \"Name\""); strcat(descbuf, "\nORDER BY \"Name\"");
...@@ -480,11 +494,14 @@ objectDescription(const char * object, PsqlSettings *pset) ...@@ -480,11 +494,14 @@ objectDescription(const char * object, PsqlSettings *pset)
* and pass it to the underlying printTable() function. * and pass it to the underlying printTable() function.
* *
*/ */
static void * xmalloc(size_t size) static void *
xmalloc(size_t size)
{ {
void * tmp; void *tmp;
tmp = malloc(size); tmp = malloc(size);
if (!tmp) { if (!tmp)
{
perror("malloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -493,19 +510,21 @@ static void * xmalloc(size_t size) ...@@ -493,19 +510,21 @@ static void * xmalloc(size_t size)
bool bool
describeTableDetails(const char * name, PsqlSettings * pset) describeTableDetails(const char *name, PsqlSettings *pset)
{ {
char descbuf[512 + NAMEDATALEN]; char descbuf[512 + NAMEDATALEN];
PGresult *res = NULL, *res2 = NULL, *res3 = NULL; PGresult *res = NULL,
*res2 = NULL,
*res3 = NULL;
printTableOpt myopt = pset->popt.topt; printTableOpt myopt = pset->popt.topt;
bool description = GetVariableBool(pset->vars, "description"); bool description = GetVariableBool(pset->vars, "description");
int i; int i;
char * view_def = NULL; char *view_def = NULL;
char * headers[5]; char *headers[5];
char ** cells = NULL; char **cells = NULL;
char * title = NULL; char *title = NULL;
char ** footers = NULL; char **footers = NULL;
char ** ptr; char **ptr;
unsigned int cols; unsigned int cols;
cols = 3 + (description ? 1 : 0); cols = 3 + (description ? 1 : 0);
...@@ -513,7 +532,8 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -513,7 +532,8 @@ describeTableDetails(const char * name, PsqlSettings * pset)
headers[0] = "Attribute"; headers[0] = "Attribute";
headers[1] = "Type"; headers[1] = "Type";
headers[2] = "Info"; headers[2] = "Info";
if (description) { if (description)
{
headers[3] = "Description"; headers[3] = "Description";
headers[4] = NULL; headers[4] = NULL;
} }
...@@ -535,7 +555,8 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -535,7 +555,8 @@ describeTableDetails(const char * name, PsqlSettings * pset)
return false; return false;
/* Did we get anything? */ /* Did we get anything? */
if (PQntuples(res)==0) { if (PQntuples(res) == 0)
{
if (!GetVariableBool(pset->vars, "quiet")) if (!GetVariableBool(pset->vars, "quiet"))
fprintf(stdout, "Did not find any class named \"%s\".\n", name); fprintf(stdout, "Did not find any class named \"%s\".\n", name);
PQclear(res); PQclear(res);
...@@ -551,44 +572,49 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -551,44 +572,49 @@ describeTableDetails(const char * name, PsqlSettings * pset)
return false; return false;
if (PQntuples(res2) > 0) if (PQntuples(res2) > 0)
view_def = PQgetvalue(res2,0,0); view_def = PQgetvalue(res2, 0, 0);
/* Generate table cells to be printed */ /* Generate table cells to be printed */
cells = calloc(PQntuples(res) * cols + 1, sizeof(*cells)); cells = calloc(PQntuples(res) * cols + 1, sizeof(*cells));
if (!cells) { if (!cells)
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (i = 0; i < PQntuples(res); i++) { for (i = 0; i < PQntuples(res); i++)
{
int4 attypmod = atoi(PQgetvalue(res, i, 3)); int4 attypmod = atoi(PQgetvalue(res, i, 3));
char * attype = PQgetvalue(res, i, 1); char *attype = PQgetvalue(res, i, 1);
/* Name */ /* Name */
cells[i*cols + 0] = PQgetvalue(res, i, 0); /* don't free this afterwards */ cells[i * cols + 0] = PQgetvalue(res, i, 0); /* don't free this
* afterwards */
/* Type */ /* Type */
cells[i*cols + 1] = xmalloc(NAMEDATALEN + 16); cells[i * cols + 1] = xmalloc(NAMEDATALEN + 16);
if (strcmp(attype, "bpchar")==0) if (strcmp(attype, "bpchar") == 0)
sprintf(cells[i*cols + 1], "char(%d)", attypmod != -1 ? attypmod - VARHDRSZ : 0); sprintf(cells[i * cols + 1], "char(%d)", attypmod != -1 ? attypmod - VARHDRSZ : 0);
else if (strcmp(attype, "varchar")==0) else if (strcmp(attype, "varchar") == 0)
sprintf(cells[i*cols + 1], "varchar(%d)", attypmod != -1 ? attypmod - VARHDRSZ : 0); sprintf(cells[i * cols + 1], "varchar(%d)", attypmod != -1 ? attypmod - VARHDRSZ : 0);
else if (strcmp(attype, "numeric")==0) else if (strcmp(attype, "numeric") == 0)
sprintf(cells[i*cols + 1], "numeric(%d,%d)", ((attypmod - VARHDRSZ) >> 16) & 0xffff, sprintf(cells[i * cols + 1], "numeric(%d,%d)", ((attypmod - VARHDRSZ) >> 16) & 0xffff,
(attypmod - VARHDRSZ) & 0xffff ); (attypmod - VARHDRSZ) & 0xffff);
else if (attype[0] == '_') else if (attype[0] == '_')
sprintf(cells[i*cols + 1], "%s[]", attype+1); sprintf(cells[i * cols + 1], "%s[]", attype + 1);
else else
strcpy(cells[i*cols + 1], attype); strcpy(cells[i * cols + 1], attype);
/* Info */ /* Info */
cells[i*cols + 2] = xmalloc(128 + 128); /* I'm cutting off the default string at 128 */ cells[i * cols + 2] = xmalloc(128 + 128); /* I'm cutting off the
cells[i*cols + 2][0] = '\0'; * default string at 128 */
cells[i * cols + 2][0] = '\0';
if (strcmp(PQgetvalue(res, i, 4), "t") == 0) if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
strcat(cells[i*cols + 2], "not null"); strcat(cells[i * cols + 2], "not null");
if (strcmp(PQgetvalue(res, i, 5), "t") == 0) { if (strcmp(PQgetvalue(res, i, 5), "t") == 0)
{
/* handle "default" here */ /* handle "default" here */
strcpy(descbuf, "SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c\n" strcpy(descbuf, "SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c\n"
"WHERE c.relname = '"); "WHERE c.relname = '");
...@@ -597,15 +623,17 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -597,15 +623,17 @@ describeTableDetails(const char * name, PsqlSettings * pset)
strcat(descbuf, PQgetvalue(res, i, 6)); strcat(descbuf, PQgetvalue(res, i, 6));
res3 = PSQLexec(pset, descbuf); res3 = PSQLexec(pset, descbuf);
if (!res) return false; if (!res)
if (cells[i*cols+2][0]) strcat(cells[i*cols+2], " "); return false;
strcat(cells[i*cols + 2], "default "); if (cells[i * cols + 2][0])
strcat(cells[i*cols + 2], PQgetvalue(res3, 0, 0)); strcat(cells[i * cols + 2], " ");
strcat(cells[i * cols + 2], "default ");
strcat(cells[i * cols + 2], PQgetvalue(res3, 0, 0));
} }
/* Description */ /* Description */
if (description) if (description)
cells[i*cols + 3] = PQgetvalue(res, i, 7); cells[i * cols + 3] = PQgetvalue(res, i, 7);
} }
/* Make title */ /* Make title */
...@@ -616,13 +644,15 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -616,13 +644,15 @@ describeTableDetails(const char * name, PsqlSettings * pset)
sprintf(title, "Table \"%s\"", name); sprintf(title, "Table \"%s\"", name);
/* Make footers */ /* Make footers */
if (view_def) { if (view_def)
{
footers = xmalloc(2 * sizeof(*footers)); footers = xmalloc(2 * sizeof(*footers));
footers[0] = xmalloc(20 + strlen(view_def)); footers[0] = xmalloc(20 + strlen(view_def));
sprintf(footers[0], "View definition: %s", view_def); sprintf(footers[0], "View definition: %s", view_def);
footers[1] = NULL; footers[1] = NULL;
} }
else { else
{
/* display indices */ /* display indices */
strcpy(descbuf, "SELECT c2.relname\n" strcpy(descbuf, "SELECT c2.relname\n"
"FROM pg_class c, pg_class c2, pg_index i\n" "FROM pg_class c, pg_class c2, pg_index i\n"
...@@ -634,14 +664,16 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -634,14 +664,16 @@ describeTableDetails(const char * name, PsqlSettings * pset)
if (!res3) if (!res3)
return false; return false;
if (PQntuples(res3) > 0) { if (PQntuples(res3) > 0)
{
footers = xmalloc((PQntuples(res3) + 1) * sizeof(*footers)); footers = xmalloc((PQntuples(res3) + 1) * sizeof(*footers));
for (i=0; i<PQntuples(res3); i++) { for (i = 0; i < PQntuples(res3); i++)
{
footers[i] = xmalloc(10 + NAMEDATALEN); footers[i] = xmalloc(10 + NAMEDATALEN);
if (PQntuples(res3)==1) if (PQntuples(res3) == 1)
sprintf(footers[i], "Index: %s", PQgetvalue(res3, i, 0)); sprintf(footers[i], "Index: %s", PQgetvalue(res3, i, 0));
else if (i==0) else if (i == 0)
sprintf(footers[i], "Indices: %s", PQgetvalue(res3, i, 0)); sprintf(footers[i], "Indices: %s", PQgetvalue(res3, i, 0));
else else
sprintf(footers[i], " %s", PQgetvalue(res3, i, 0)); sprintf(footers[i], " %s", PQgetvalue(res3, i, 0));
...@@ -658,9 +690,10 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -658,9 +690,10 @@ describeTableDetails(const char * name, PsqlSettings * pset)
/* clean up */ /* clean up */
free(title); free(title);
for (i = 0; i<PQntuples(res); i++) { for (i = 0; i < PQntuples(res); i++)
free(cells[i*cols + 1]); {
free(cells[i*cols + 2]); free(cells[i * cols + 1]);
free(cells[i * cols + 2]);
} }
free(cells); free(cells);
...@@ -691,10 +724,10 @@ describeTableDetails(const char * name, PsqlSettings * pset) ...@@ -691,10 +724,10 @@ describeTableDetails(const char * name, PsqlSettings * pset)
* (any order of the above is fine) * (any order of the above is fine)
*/ */
bool bool
listTables(const char * infotype, const char * name, PsqlSettings * pset) listTables(const char *infotype, const char *name, PsqlSettings *pset)
{ {
bool showTables = strchr(infotype, 't') != NULL; bool showTables = strchr(infotype, 't') != NULL;
bool showIndices= strchr(infotype, 'i') != NULL; bool showIndices = strchr(infotype, 'i') != NULL;
bool showViews = strchr(infotype, 'v') != NULL; bool showViews = strchr(infotype, 'v') != NULL;
bool showSeq = strchr(infotype, 's') != NULL; bool showSeq = strchr(infotype, 's') != NULL;
bool showSystem = strchr(infotype, 'S') != NULL; bool showSystem = strchr(infotype, 'S') != NULL;
...@@ -708,7 +741,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -708,7 +741,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
descbuf[0] = '\0'; descbuf[0] = '\0';
/* tables */ /* tables */
if (showTables) { if (showTables)
{
strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'table'::text as \"Type\""); strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'table'::text as \"Type\"");
if (description) if (description)
strcat(descbuf, ", obj_description(c.oid) as \"Description\""); strcat(descbuf, ", obj_description(c.oid) as \"Description\"");
...@@ -716,7 +750,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -716,7 +750,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
"WHERE c.relowner = u.usesysid AND c.relkind = 'r'\n" "WHERE c.relowner = u.usesysid AND c.relkind = 'r'\n"
" AND not exists (select 1 from pg_views where viewname = c.relname)\n"); " AND not exists (select 1 from pg_views where viewname = c.relname)\n");
strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n");
if (name) { if (name)
{
strcat(descbuf, " AND c.relname ~ '^"); strcat(descbuf, " AND c.relname ~ '^");
strncat(descbuf, name, REGEXP_CUTOFF); strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "'\n"); strcat(descbuf, "'\n");
...@@ -724,7 +759,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -724,7 +759,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
} }
/* views */ /* views */
if (showViews) { if (showViews)
{
if (descbuf[0]) if (descbuf[0])
strcat(descbuf, "\nUNION\n\n"); strcat(descbuf, "\nUNION\n\n");
...@@ -735,7 +771,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -735,7 +771,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
"WHERE c.relowner = u.usesysid AND c.relkind = 'r'\n" "WHERE c.relowner = u.usesysid AND c.relkind = 'r'\n"
" AND exists (select 1 from pg_views where viewname = c.relname)\n"); " AND exists (select 1 from pg_views where viewname = c.relname)\n");
strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n");
if (name) { if (name)
{
strcat(descbuf, " AND c.relname ~ '^"); strcat(descbuf, " AND c.relname ~ '^");
strncat(descbuf, name, REGEXP_CUTOFF); strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "'\n"); strcat(descbuf, "'\n");
...@@ -743,7 +780,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -743,7 +780,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
} }
/* indices, sequences */ /* indices, sequences */
if (showIndices || showSeq) { if (showIndices || showSeq)
{
if (descbuf[0]) if (descbuf[0])
strcat(descbuf, "\nUNION\n\n"); strcat(descbuf, "\nUNION\n\n");
...@@ -766,7 +804,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -766,7 +804,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
strcat(descbuf, " AND (c.relkind != 'i' OR c.relname !~ '^xinx')\n"); strcat(descbuf, " AND (c.relkind != 'i' OR c.relname !~ '^xinx')\n");
strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n");
if (name) { if (name)
{
strcat(descbuf, " AND c.relname ~ '^"); strcat(descbuf, " AND c.relname ~ '^");
strncat(descbuf, name, REGEXP_CUTOFF); strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "'\n"); strcat(descbuf, "'\n");
...@@ -774,7 +813,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -774,7 +813,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
} }
/* real system catalogue tables */ /* real system catalogue tables */
if (showSystem && showTables) { if (showSystem && showTables)
{
if (descbuf[0]) if (descbuf[0])
strcat(descbuf, "\nUNION\n\n"); strcat(descbuf, "\nUNION\n\n");
...@@ -783,7 +823,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -783,7 +823,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
strcat(descbuf, ", obj_description(c.oid) as \"Description\""); strcat(descbuf, ", obj_description(c.oid) as \"Description\"");
strcat(descbuf, "\nFROM pg_class c, pg_user u\n" strcat(descbuf, "\nFROM pg_class c, pg_user u\n"
"WHERE c.relowner = u.usesysid AND c.relkind = 's'\n"); "WHERE c.relowner = u.usesysid AND c.relkind = 's'\n");
if (name) { if (name)
{
strcat(descbuf, " AND c.relname ~ '^"); strcat(descbuf, " AND c.relname ~ '^");
strncat(descbuf, name, REGEXP_CUTOFF); strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "'\n"); strcat(descbuf, "'\n");
...@@ -800,7 +841,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset) ...@@ -800,7 +841,8 @@ listTables(const char * infotype, const char * name, PsqlSettings * pset)
if (PQntuples(res) == 0) if (PQntuples(res) == 0)
fprintf(pset->queryFout, "No matching classes found.\n"); fprintf(pset->queryFout, "No matching classes found.\n");
else { else
{
myopt.topt.tuples_only = false; myopt.topt.tuples_only = false;
myopt.nullPrint = NULL; myopt.nullPrint = NULL;
myopt.title = "List of classes"; myopt.title = "List of classes";
......
...@@ -5,38 +5,38 @@ ...@@ -5,38 +5,38 @@
/* \da */ /* \da */
bool bool
describeAggregates(const char * name, PsqlSettings * pset); describeAggregates(const char *name, PsqlSettings *pset);
/* \df */ /* \df */
bool bool
describeFunctions(const char * name, PsqlSettings * pset); describeFunctions(const char *name, PsqlSettings *pset);
/* \dT */ /* \dT */
bool bool
describeTypes(const char * name, PsqlSettings * pset); describeTypes(const char *name, PsqlSettings *pset);
/* \do */ /* \do */
bool bool
describeOperators(const char * name, PsqlSettings * pset); describeOperators(const char *name, PsqlSettings *pset);
/* \dp (formerly \z) */ /* \dp (formerly \z) */
bool bool
permissionsList(const char * name, PsqlSettings *pset); permissionsList(const char *name, PsqlSettings *pset);
/* \dd */ /* \dd */
bool bool
objectDescription(const char * object, PsqlSettings *pset); objectDescription(const char *object, PsqlSettings *pset);
/* \d foo */ /* \d foo */
bool bool
describeTableDetails(const char * name, PsqlSettings * pset); describeTableDetails(const char *name, PsqlSettings *pset);
/* \l */ /* \l */
bool bool
listAllDbs(PsqlSettings *pset); listAllDbs(PsqlSettings *pset);
/* \dt, \di, \dS, etc. */ /* \dt, \di, \dS, etc. */
bool bool
listTables(const char * infotype, const char * name, PsqlSettings * pset); listTables(const char *infotype, const char *name, PsqlSettings *pset);
#endif /* DESCRIBE_H */ #endif /* DESCRIBE_H */
...@@ -34,21 +34,27 @@ ...@@ -34,21 +34,27 @@
*/ */
#define ON(var) (var ? "on" : "off") #define ON(var) (var ? "on" : "off")
void usage(void) void
usage(void)
{ {
const char *env; const char *env;
const char *user; const char *user;
#ifndef WIN32 #ifndef WIN32
struct passwd *pw = NULL; struct passwd *pw = NULL;
#endif #endif
/* Find default user, in case we need it. */ /* Find default user, in case we need it. */
user = getenv("USER"); user = getenv("USER");
if (!user) { if (!user)
{
#ifndef WIN32 #ifndef WIN32
pw = getpwuid(getuid()); pw = getpwuid(getuid());
if (pw) user = pw->pw_name; if (pw)
else { user = pw->pw_name;
else
{
perror("getpwuid()"); perror("getpwuid()");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -64,7 +70,8 @@ void usage(void) ...@@ -64,7 +70,8 @@ void usage(void)
/* Display default database */ /* Display default database */
env = getenv("PGDATABASE"); env = getenv("PGDATABASE");
if (!env) env=user; if (!env)
env = user;
fprintf(stderr, " -d dbname Specify database name to connect to (default: %s)\n", env); fprintf(stderr, " -d dbname Specify database name to connect to (default: %s)\n", env);
fprintf(stderr, " -e Echo all input in non-interactive mode\n"); fprintf(stderr, " -e Echo all input in non-interactive mode\n");
...@@ -101,7 +108,8 @@ void usage(void) ...@@ -101,7 +108,8 @@ void usage(void)
/* Display default user */ /* Display default user */
env = getenv("PGUSER"); env = getenv("PGUSER");
if (!env) env=user; if (!env)
env = user;
fprintf(stderr, " -U [username] Specifiy username, \"?\"=prompt (default user: %s)\n", env); fprintf(stderr, " -U [username] Specifiy username, \"?\"=prompt (default user: %s)\n", env);
fprintf(stderr, " -x Turn on expanded table output (-P expanded)\n"); fprintf(stderr, " -x Turn on expanded table output (-P expanded)\n");
...@@ -112,7 +120,8 @@ void usage(void) ...@@ -112,7 +120,8 @@ void usage(void)
fprintf(stderr, "Consult the documentation for the complete details.\n"); fprintf(stderr, "Consult the documentation for the complete details.\n");
#ifndef WIN32 #ifndef WIN32
if (pw) free(pw); if (pw)
free(pw);
#endif #endif
} }
...@@ -125,10 +134,12 @@ void usage(void) ...@@ -125,10 +134,12 @@ void usage(void)
*/ */
#ifndef TIOCGWINSZ #ifndef TIOCGWINSZ
struct winsize { struct winsize
{
int ws_row; int ws_row;
int ws_col; int ws_col;
}; };
#endif #endif
void void
...@@ -197,7 +208,8 @@ slashUsage(PsqlSettings *pset) ...@@ -197,7 +208,8 @@ slashUsage(PsqlSettings *pset)
fprintf(fout, " \\z -- list table access permissions\n"); fprintf(fout, " \\z -- list table access permissions\n");
fprintf(fout, " \\! [<cmd>] -- shell escape or command\n"); fprintf(fout, " \\! [<cmd>] -- shell escape or command\n");
if (usePipe) { if (usePipe)
{
pclose(fout); pclose(fout);
pqsignal(SIGPIPE, SIG_DFL); pqsignal(SIGPIPE, SIG_DFL);
} }
...@@ -212,7 +224,7 @@ slashUsage(PsqlSettings *pset) ...@@ -212,7 +224,7 @@ slashUsage(PsqlSettings *pset)
void void
helpSQL(const char *topic) helpSQL(const char *topic)
{ {
if (!topic || strlen(topic)==0) if (!topic || strlen(topic) == 0)
{ {
char left_center_right; /* Which column we're displaying */ char left_center_right; /* Which column we're displaying */
int i; /* Index into QL_HELP[] */ int i; /* Index into QL_HELP[] */
...@@ -274,33 +286,33 @@ void ...@@ -274,33 +286,33 @@ void
print_copyright(void) print_copyright(void)
{ {
puts( puts(
" "
PostgreSQL Data Base Management System PostgreSQL Data Base Management System
Copyright (c) 1996-9 PostgreSQL Global Development Group Copyright(c) 1996 - 9 PostgreSQL Global Development Group
This software is based on Postgres95, formerly known as Postgres, which This software is based on Postgres95, formerly known as Postgres, which
contains the following notice: contains the following notice:
Copyright (c) 1994-7 Regents of the University of California Copyright(c) 1994 - 7 Regents of the University of California
Permission to use, copy, modify, and distribute this software and its Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement documentation for any purpose, without fee, and without a written agreement
is hereby granted, provided that the above copyright notice and this paragraph is hereby granted, provided that the above copyright notice and this paragraph
and the following two paragraphs appear in all copies. and the following two paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN \"AS IS\" BASIS, PARTICULAR PURPOSE.THE SOFTWARE PROVIDED HEREUNDER IS ON AN \ "AS IS\" BASIS,
AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
(end of terms)" (end of terms) "
); );
} }
...@@ -13,4 +13,3 @@ void print_copyright(void); ...@@ -13,4 +13,3 @@ void print_copyright(void);
#endif #endif
...@@ -10,9 +10,11 @@ ...@@ -10,9 +10,11 @@
/* (of course there is no runtime command for doing that :) */ /* (of course there is no runtime command for doing that :) */
#ifdef USE_READLINE #ifdef USE_READLINE
static bool useReadline; static bool useReadline;
#endif #endif
#ifdef USE_HISTORY #ifdef USE_HISTORY
static bool useHistory; static bool useHistory;
#endif #endif
...@@ -25,15 +27,17 @@ static bool useHistory; ...@@ -25,15 +27,17 @@ static bool useHistory;
char * char *
gets_interactive(const char *prompt) gets_interactive(const char *prompt)
{ {
char * s; char *s;
#ifdef USE_READLINE #ifdef USE_READLINE
if (useReadline) { if (useReadline)
{
s = readline(prompt); s = readline(prompt);
fputc('\r', stdout); fputc('\r', stdout);
fflush(stdout); fflush(stdout);
} }
else { else
{
#endif #endif
fputs(prompt, stdout); fputs(prompt, stdout);
fflush(stdout); fflush(stdout);
...@@ -65,10 +69,12 @@ gets_fromFile(FILE *source) ...@@ -65,10 +69,12 @@ gets_fromFile(FILE *source)
initPQExpBuffer(&buffer); initPQExpBuffer(&buffer);
while (fgets(line, 1024, source) != NULL) { while (fgets(line, 1024, source) != NULL)
{
appendPQExpBufferStr(&buffer, line); appendPQExpBufferStr(&buffer, line);
if (buffer.data[buffer.len-1] == '\n') { if (buffer.data[buffer.len - 1] == '\n')
buffer.data[buffer.len-1] = '\0'; {
buffer.data[buffer.len - 1] = '\0';
return buffer.data; return buffer.data;
} }
} }
...@@ -93,22 +99,27 @@ void ...@@ -93,22 +99,27 @@ void
initializeInput(int flags) initializeInput(int flags)
{ {
#ifdef USE_READLINE #ifdef USE_READLINE
if (flags == 1) { if (flags == 1)
{
useReadline = true; useReadline = true;
rl_readline_name = "psql"; rl_readline_name = "psql";
} }
#endif #endif
#ifdef USE_HISTORY #ifdef USE_HISTORY
if (flags == 1) { if (flags == 1)
const char * home; {
const char *home;
useHistory = true; useHistory = true;
using_history(); using_history();
home = getenv("HOME"); home = getenv("HOME");
if (home) { if (home)
char * psql_history = (char *) malloc(strlen(home) + 20); {
if (psql_history) { char *psql_history = (char *) malloc(strlen(home) + 20);
if (psql_history)
{
sprintf(psql_history, "%s/.psql_history", home); sprintf(psql_history, "%s/.psql_history", home);
read_history(psql_history); read_history(psql_history);
free(psql_history); free(psql_history);
...@@ -124,8 +135,10 @@ bool ...@@ -124,8 +135,10 @@ bool
saveHistory(const char *fname) saveHistory(const char *fname)
{ {
#ifdef USE_HISTORY #ifdef USE_HISTORY
if (useHistory) { if (useHistory)
if (write_history(fname) != 0) { {
if (write_history(fname) != 0)
{
perror(fname); perror(fname);
return false; return false;
} }
...@@ -144,14 +157,17 @@ void ...@@ -144,14 +157,17 @@ void
finishInput(void) finishInput(void)
{ {
#ifdef USE_HISTORY #ifdef USE_HISTORY
if (useHistory) { if (useHistory)
char * home; {
char * psql_history; char *home;
char *psql_history;
home = getenv("HOME"); home = getenv("HOME");
if (home) { if (home)
{
psql_history = (char *) malloc(strlen(home) + 20); psql_history = (char *) malloc(strlen(home) + 20);
if (psql_history) { if (psql_history)
{
sprintf(psql_history, "%s/.psql_history", home); sprintf(psql_history, "%s/.psql_history", home);
write_history(psql_history); write_history(psql_history);
free(psql_history); free(psql_history);
......
...@@ -38,19 +38,19 @@ ...@@ -38,19 +38,19 @@
char * char *
gets_interactive(const char *prompt); gets_interactive(const char *prompt);
char * char *
gets_fromFile(FILE *source); gets_fromFile(FILE *source);
void void
initializeInput(int flags); initializeInput(int flags);
bool bool
saveHistory(const char *fname); saveHistory(const char *fname);
void void
finishInput(void); finishInput(void);
#endif #endif
...@@ -24,25 +24,26 @@ ...@@ -24,25 +24,26 @@
static char notice[80]; static char notice[80];
static void static void
_my_notice_handler(void * arg, const char * message) { _my_notice_handler(void *arg, const char *message)
(void)arg; {
(void) arg;
strncpy(notice, message, 79); strncpy(notice, message, 79);
notice[79] = '\0'; notice[79] = '\0';
} }
static bool static bool
handle_transaction(PsqlSettings * pset) handle_transaction(PsqlSettings *pset)
{ {
const char * var = GetVariable(pset->vars, "lo_transaction"); const char *var = GetVariable(pset->vars, "lo_transaction");
PGresult * res; PGresult *res;
bool commit; bool commit;
PQnoticeProcessor old_notice_hook; PQnoticeProcessor old_notice_hook;
if (var && strcmp(var, "nothing")==0) if (var && strcmp(var, "nothing") == 0)
return true; return true;
commit = (var && strcmp(var, "commit")==0); commit = (var && strcmp(var, "commit") == 0);
notice[0] = '\0'; notice[0] = '\0';
old_notice_hook = PQsetNoticeProcessor(pset->db, _my_notice_handler, NULL); old_notice_hook = PQsetNoticeProcessor(pset->db, _my_notice_handler, NULL);
...@@ -51,12 +52,14 @@ handle_transaction(PsqlSettings * pset) ...@@ -51,12 +52,14 @@ handle_transaction(PsqlSettings * pset)
if (!res) if (!res)
return false; return false;
if (notice[0]) { if (notice[0])
if ( (!commit && strcmp(notice, "NOTICE: UserAbortTransactionBlock and not in in-progress state\n")!=0) || {
(commit && strcmp(notice, "NOTICE: EndTransactionBlock and not inprogress/abort state\n")!=0) ) if ((!commit && strcmp(notice, "NOTICE: UserAbortTransactionBlock and not in in-progress state\n") != 0) ||
(commit && strcmp(notice, "NOTICE: EndTransactionBlock and not inprogress/abort state\n") != 0))
fputs(notice, stderr); fputs(notice, stderr);
} }
else if (!GetVariableBool(pset->vars, "quiet")) { else if (!GetVariableBool(pset->vars, "quiet"))
{
if (commit) if (commit)
puts("Warning: Your transaction in progress has been committed."); puts("Warning: Your transaction in progress has been committed.");
else else
...@@ -75,22 +78,24 @@ handle_transaction(PsqlSettings * pset) ...@@ -75,22 +78,24 @@ handle_transaction(PsqlSettings * pset)
* Write a large object to a file * Write a large object to a file
*/ */
bool bool
do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_arg) do_lo_export(PsqlSettings *pset, const char *loid_arg, const char *filename_arg)
{ {
PGresult * res; PGresult *res;
int status; int status;
bool own_transaction = true; bool own_transaction = true;
const char * var = GetVariable(pset->vars, "lo_transaction"); const char *var = GetVariable(pset->vars, "lo_transaction");
if (var && strcmp(var, "nothing")==0) if (var && strcmp(var, "nothing") == 0)
own_transaction = false; own_transaction = false;
if (!pset->db) { if (!pset->db)
{
fputs("You are not connected to a database.\n", stderr); fputs("You are not connected to a database.\n", stderr);
return false; return false;
} }
if (own_transaction) { if (own_transaction)
{
if (!handle_transaction(pset)) if (!handle_transaction(pset))
return false; return false;
...@@ -100,18 +105,23 @@ do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_a ...@@ -100,18 +105,23 @@ do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_a
PQclear(res); PQclear(res);
} }
status = lo_export(pset->db, atol(loid_arg), (char *)filename_arg); status = lo_export(pset->db, atol(loid_arg), (char *) filename_arg);
if (status != 1) { /* of course this status is documented nowhere :( */ if (status != 1)
{ /* of course this status is documented
* nowhere :( */
fputs(PQerrorMessage(pset->db), stderr); fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) { if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
} }
return false; return false;
} }
if (own_transaction) { if (own_transaction)
if (!(res = PSQLexec(pset, "COMMIT"))) { {
if (!(res = PSQLexec(pset, "COMMIT")))
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
return false; return false;
...@@ -133,24 +143,26 @@ do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_a ...@@ -133,24 +143,26 @@ do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_a
* Copy large object from file to database * Copy large object from file to database
*/ */
bool bool
do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * comment_arg) do_lo_import(PsqlSettings *pset, const char *filename_arg, const char *comment_arg)
{ {
PGresult * res; PGresult *res;
Oid loid; Oid loid;
char buf[1024]; char buf[1024];
unsigned int i; unsigned int i;
bool own_transaction = true; bool own_transaction = true;
const char * var = GetVariable(pset->vars, "lo_transaction"); const char *var = GetVariable(pset->vars, "lo_transaction");
if (var && strcmp(var, "nothing")==0) if (var && strcmp(var, "nothing") == 0)
own_transaction = false; own_transaction = false;
if (!pset->db) { if (!pset->db)
{
fputs("You are not connected to a database.\n", stderr); fputs("You are not connected to a database.\n", stderr);
return false; return false;
} }
if (own_transaction) { if (own_transaction)
{
if (!handle_transaction(pset)) if (!handle_transaction(pset))
return false; return false;
...@@ -160,10 +172,12 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen ...@@ -160,10 +172,12 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen
PQclear(res); PQclear(res);
} }
loid = lo_import(pset->db, (char *)filename_arg); loid = lo_import(pset->db, (char *) filename_arg);
if (loid == InvalidOid) { if (loid == InvalidOid)
{
fputs(PQerrorMessage(pset->db), stderr); fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) { if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
} }
...@@ -171,17 +185,20 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen ...@@ -171,17 +185,20 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen
} }
/* insert description if given */ /* insert description if given */
if (comment_arg) { if (comment_arg)
{
sprintf(buf, "INSERT INTO pg_description VALUES (%d, '", loid); sprintf(buf, "INSERT INTO pg_description VALUES (%d, '", loid);
for (i=0; i<strlen(comment_arg); i++) for (i = 0; i < strlen(comment_arg); i++)
if (comment_arg[i]=='\'') if (comment_arg[i] == '\'')
strcat(buf, "\\'"); strcat(buf, "\\'");
else else
strncat(buf, &comment_arg[i], 1); strncat(buf, &comment_arg[i], 1);
strcat(buf, "')"); strcat(buf, "')");
if (!(res = PSQLexec(pset, buf))) { if (!(res = PSQLexec(pset, buf)))
if (own_transaction) { {
if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
} }
...@@ -189,8 +206,10 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen ...@@ -189,8 +206,10 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen
} }
} }
if (own_transaction) { if (own_transaction)
if (!(res = PSQLexec(pset, "COMMIT"))) { {
if (!(res = PSQLexec(pset, "COMMIT")))
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
return false; return false;
...@@ -212,24 +231,27 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen ...@@ -212,24 +231,27 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen
* *
* removes a large object out of the database * removes a large object out of the database
*/ */
bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg) bool
do_lo_unlink(PsqlSettings *pset, const char *loid_arg)
{ {
PGresult * res; PGresult *res;
int status; int status;
Oid loid = (Oid)atol(loid_arg); Oid loid = (Oid) atol(loid_arg);
char buf[256]; char buf[256];
bool own_transaction = true; bool own_transaction = true;
const char * var = GetVariable(pset->vars, "lo_transaction"); const char *var = GetVariable(pset->vars, "lo_transaction");
if (var && strcmp(var, "nothing")==0) if (var && strcmp(var, "nothing") == 0)
own_transaction = false; own_transaction = false;
if (!pset->db) { if (!pset->db)
{
fputs("You are not connected to a database.\n", stderr); fputs("You are not connected to a database.\n", stderr);
return false; return false;
} }
if (own_transaction) { if (own_transaction)
{
if (!handle_transaction(pset)) if (!handle_transaction(pset))
return false; return false;
...@@ -240,9 +262,11 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg) ...@@ -240,9 +262,11 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
} }
status = lo_unlink(pset->db, loid); status = lo_unlink(pset->db, loid);
if (status == -1) { if (status == -1)
{
fputs(PQerrorMessage(pset->db), stderr); fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) { if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
} }
...@@ -251,8 +275,10 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg) ...@@ -251,8 +275,10 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
/* remove the comment as well */ /* remove the comment as well */
sprintf(buf, "DELETE FROM pg_description WHERE objoid = %d", loid); sprintf(buf, "DELETE FROM pg_description WHERE objoid = %d", loid);
if (!(res = PSQLexec(pset, buf))) { if (!(res = PSQLexec(pset, buf)))
if (own_transaction) { {
if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
} }
...@@ -260,8 +286,10 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg) ...@@ -260,8 +286,10 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
} }
if (own_transaction) { if (own_transaction)
if (!(res = PSQLexec(pset, "COMMIT"))) { {
if (!(res = PSQLexec(pset, "COMMIT")))
{
res = PQexec(pset->db, "ROLLBACK"); res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
return false; return false;
...@@ -282,9 +310,10 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg) ...@@ -282,9 +310,10 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
* *
* Show all large objects in database, with comments if desired * Show all large objects in database, with comments if desired
*/ */
bool do_lo_list(PsqlSettings * pset) bool
do_lo_list(PsqlSettings *pset)
{ {
PGresult * res; PGresult *res;
char descbuf[512]; char descbuf[512];
printQueryOpt myopt = pset->popt; printQueryOpt myopt = pset->popt;
...@@ -292,7 +321,7 @@ bool do_lo_list(PsqlSettings * pset) ...@@ -292,7 +321,7 @@ bool do_lo_list(PsqlSettings * pset)
strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\""); strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
if (GetVariableBool(pset->vars, "description")) if (GetVariableBool(pset->vars, "description"))
strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\""); strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\"");
strcat(descbuf,"\nFROM pg_class, pg_user\n" strcat(descbuf, "\nFROM pg_class, pg_user\n"
"WHERE usesysid = relowner AND relkind = 'l'\n" "WHERE usesysid = relowner AND relkind = 'l'\n"
"ORDER BY \"ID\""); "ORDER BY \"ID\"");
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
#include "settings.h" #include "settings.h"
bool do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_arg); bool do_lo_export(PsqlSettings *pset, const char *loid_arg, const char *filename_arg);
bool do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * comment_arg); bool do_lo_import(PsqlSettings *pset, const char *filename_arg, const char *comment_arg);
bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg); bool do_lo_unlink(PsqlSettings *pset, const char *loid_arg);
bool do_lo_list(PsqlSettings * pset); bool do_lo_list(PsqlSettings *pset);
#endif /* LARGE_OBJ_H */ #endif /* LARGE_OBJ_H */
...@@ -40,7 +40,9 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -40,7 +40,9 @@ MainLoop(PsqlSettings *pset, FILE *source)
int paren_level; int paren_level;
unsigned int query_start; unsigned int query_start;
int i, prevlen, thislen; int i,
prevlen,
thislen;
/* Save the prior command source */ /* Save the prior command source */
FILE *prev_cmd_source; FILE *prev_cmd_source;
...@@ -60,7 +62,8 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -60,7 +62,8 @@ MainLoop(PsqlSettings *pset, FILE *source)
query_buf = createPQExpBuffer(); query_buf = createPQExpBuffer();
if (!query_buf) { if (!query_buf)
{
perror("createPQExpBuffer"); perror("createPQExpBuffer");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -76,6 +79,7 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -76,6 +79,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
{ {
if (slashCmdStatus == CMD_NEWEDIT) if (slashCmdStatus == CMD_NEWEDIT)
{ {
/* /*
* just returned from editing the line? then just copy to the * just returned from editing the line? then just copy to the
* input buffer * input buffer
...@@ -89,9 +93,10 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -89,9 +93,10 @@ MainLoop(PsqlSettings *pset, FILE *source)
} }
else else
{ {
/* /*
* otherwise, set interactive prompt if necessary * otherwise, set interactive prompt if necessary and get
* and get another line * another line
*/ */
if (pset->cur_cmd_interactive) if (pset->cur_cmd_interactive)
{ {
...@@ -100,7 +105,7 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -100,7 +105,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
if (in_quote && in_quote == '\'') if (in_quote && in_quote == '\'')
prompt_status = PROMPT_SINGLEQUOTE; prompt_status = PROMPT_SINGLEQUOTE;
else if (in_quote && in_quote == '"') else if (in_quote && in_quote == '"')
prompt_status= PROMPT_DOUBLEQUOTE; prompt_status = PROMPT_DOUBLEQUOTE;
else if (xcomment != NULL) else if (xcomment != NULL)
prompt_status = PROMPT_COMMENT; prompt_status = PROMPT_COMMENT;
else if (query_buf->len > 0) else if (query_buf->len > 0)
...@@ -121,9 +126,10 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -121,9 +126,10 @@ MainLoop(PsqlSettings *pset, FILE *source)
/* /*
* query_buf holds query already accumulated. line is the malloc'd * query_buf holds query already accumulated. line is the
* new line of input (note it must be freed before looping around!) * malloc'd new line of input (note it must be freed before
* query_start is the next command start location within the line. * looping around!) query_start is the next command start location
* within the line.
*/ */
/* No more input. Time to quit, or \i done */ /* No more input. Time to quit, or \i done */
...@@ -141,8 +147,10 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -141,8 +147,10 @@ MainLoop(PsqlSettings *pset, FILE *source)
/* strip trailing backslashes, they don't have a clear meaning */ /* strip trailing backslashes, they don't have a clear meaning */
while (1) { while (1)
char * cp = strrchr(line, '\\'); {
char *cp = strrchr(line, '\\');
if (cp && (*(cp + 1) == '\0')) if (cp && (*(cp + 1) == '\0'))
*cp = '\0'; *cp = '\0';
else else
...@@ -159,21 +167,26 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -159,21 +167,26 @@ MainLoop(PsqlSettings *pset, FILE *source)
len = strlen(line); len = strlen(line);
thislen = PQmblen(line); thislen = PQmblen(line);
for (i = 0; line[i]; i += (thislen = PQmblen(&line[i])) ) { for (i = 0; line[i]; i += (thislen = PQmblen(&line[i])))
if (interpol_char && interpol_char[0] != '\0' && interpol_char[0] == line[i]) { {
size_t in_length, out_length; if (interpol_char && interpol_char[0] != '\0' && interpol_char[0] == line[i])
const char * value; {
char * new; size_t in_length,
bool closer; /* did we have a closing delimiter or just an end of line? */ out_length;
const char *value;
in_length = strcspn(&line[i+thislen], interpol_char); char *new;
bool closer; /* did we have a closing delimiter
* or just an end of line? */
in_length = strcspn(&line[i + thislen], interpol_char);
closer = line[i + thislen + in_length] == line[i]; closer = line[i + thislen + in_length] == line[i];
line[i + thislen + in_length] = '\0'; line[i + thislen + in_length] = '\0';
value = interpolate_var(&line[i + thislen], pset); value = interpolate_var(&line[i + thislen], pset);
out_length = strlen(value); out_length = strlen(value);
new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1); new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1);
if (!new) { if (!new)
{
perror("malloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -191,7 +204,8 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -191,7 +204,8 @@ MainLoop(PsqlSettings *pset, FILE *source)
} }
/* nothing left on line? then ignore */ /* nothing left on line? then ignore */
if (line[0] == '\0') { if (line[0] == '\0')
{
free(line); free(line);
continue; continue;
} }
...@@ -204,8 +218,8 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -204,8 +218,8 @@ MainLoop(PsqlSettings *pset, FILE *source)
/* /*
* Parse line, looking for command separators. * Parse line, looking for command separators.
* *
* The current character is at line[i], the prior character at * The current character is at line[i], the prior character at line[i
* line[i - prevlen], the next character at line[i + thislen]. * - prevlen], the next character at line[i + thislen].
*/ */
prevlen = 0; prevlen = 0;
thislen = (len > 0) ? PQmblen(line) : 0; thislen = (len > 0) ? PQmblen(line) : 0;
...@@ -213,7 +227,8 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -213,7 +227,8 @@ MainLoop(PsqlSettings *pset, FILE *source)
#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i)) #define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
success = true; success = true;
for (i = 0; i < len; ADVANCE_1) { for (i = 0; i < len; ADVANCE_1)
{
if (!success && die_on_error) if (!success && die_on_error)
break; break;
...@@ -226,7 +241,8 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -226,7 +241,8 @@ MainLoop(PsqlSettings *pset, FILE *source)
/* in quote? */ /* in quote? */
if (in_quote) { if (in_quote)
{
/* end of quote */ /* end of quote */
if (line[i] == in_quote && !was_bslash) if (line[i] == in_quote && !was_bslash)
in_quote = '\0'; in_quote = '\0';
...@@ -237,15 +253,18 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -237,15 +253,18 @@ MainLoop(PsqlSettings *pset, FILE *source)
in_quote = line[i]; in_quote = line[i];
/* in extended comment? */ /* in extended comment? */
else if (xcomment != NULL) { else if (xcomment != NULL)
if (line[i] == '*' && line[i + thislen] == '/') { {
if (line[i] == '*' && line[i + thislen] == '/')
{
xcomment = NULL; xcomment = NULL;
ADVANCE_1; ADVANCE_1;
} }
} }
/* start of extended comment? */ /* start of extended comment? */
else if (line[i] == '/' && line[i + thislen] == '*') { else if (line[i] == '/' && line[i + thislen] == '*')
{
xcomment = &line[i]; xcomment = &line[i];
ADVANCE_1; ADVANCE_1;
} }
...@@ -266,11 +285,17 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -266,11 +285,17 @@ MainLoop(PsqlSettings *pset, FILE *source)
paren_level--; paren_level--;
/* semicolon? then send query */ /* semicolon? then send query */
else if (line[i] == ';' && !was_bslash && paren_level==0) { else if (line[i] == ';' && !was_bslash && paren_level == 0)
{
line[i] = '\0'; line[i] = '\0';
/* is there anything else on the line? */ /* is there anything else on the line? */
if (line[query_start + strspn(line + query_start, " \t")]!='\0') { if (line[query_start + strspn(line + query_start, " \t")] != '\0')
/* insert a cosmetic newline, if this is not the first line in the buffer */ {
/*
* insert a cosmetic newline, if this is not the first
* line in the buffer
*/
if (query_buf->len > 0) if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n'); appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */ /* append the line to the query buffer */
...@@ -285,14 +310,20 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -285,14 +310,20 @@ MainLoop(PsqlSettings *pset, FILE *source)
} }
/* backslash command */ /* backslash command */
else if (was_bslash) { else if (was_bslash)
const char * end_of_cmd = NULL; {
const char *end_of_cmd = NULL;
line[i - prevlen] = '\0'; /* overwrites backslash */ line[i - prevlen] = '\0'; /* overwrites backslash */
/* is there anything else on the line? */ /* is there anything else on the line? */
if (line[query_start + strspn(line + query_start, " \t")]!='\0') { if (line[query_start + strspn(line + query_start, " \t")] != '\0')
/* insert a cosmetic newline, if this is not the first line in the buffer */ {
/*
* insert a cosmetic newline, if this is not the first
* line in the buffer
*/
if (query_buf->len > 0) if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n'); appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */ /* append the line to the query buffer */
...@@ -305,14 +336,16 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -305,14 +336,16 @@ MainLoop(PsqlSettings *pset, FILE *source)
success = slashCmdStatus != CMD_ERROR; success = slashCmdStatus != CMD_ERROR;
if (slashCmdStatus == CMD_SEND) { if (slashCmdStatus == CMD_SEND)
{
success = SendQuery(pset, query_buf->data); success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf); resetPQExpBuffer(query_buf);
query_start = i + thislen; query_start = i + thislen;
} }
/* is there anything left after the backslash command? */ /* is there anything left after the backslash command? */
if (end_of_cmd) { if (end_of_cmd)
{
i += end_of_cmd - &line[i]; i += end_of_cmd - &line[i];
query_start = i; query_start = i;
} }
...@@ -322,20 +355,23 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -322,20 +355,23 @@ MainLoop(PsqlSettings *pset, FILE *source)
} }
if (!success && die_on_error && !pset->cur_cmd_interactive) { if (!success && die_on_error && !pset->cur_cmd_interactive)
{
successResult = EXIT_USER; successResult = EXIT_USER;
break; break;
} }
if (slashCmdStatus == CMD_TERMINATE) { if (slashCmdStatus == CMD_TERMINATE)
{
successResult = EXIT_SUCCESS; successResult = EXIT_SUCCESS;
break; break;
} }
/* Put the rest of the line in the query buffer. */ /* Put the rest of the line in the query buffer. */
if (line[query_start + strspn(line + query_start, " \t")]!='\0') { if (line[query_start + strspn(line + query_start, " \t")] != '\0')
{
if (query_buf->len > 0) if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n'); appendPQExpBufferChar(query_buf, '\n');
appendPQExpBufferStr(query_buf, line + query_start); appendPQExpBufferStr(query_buf, line + query_start);
...@@ -345,14 +381,16 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -345,14 +381,16 @@ MainLoop(PsqlSettings *pset, FILE *source)
/* In single line mode, send off the query if any */ /* In single line mode, send off the query if any */
if (query_buf->data[0] != '\0' && GetVariableBool(pset->vars, "singleline")) { if (query_buf->data[0] != '\0' && GetVariableBool(pset->vars, "singleline"))
{
success = SendQuery(pset, query_buf->data); success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf); resetPQExpBuffer(query_buf);
} }
/* Have we lost the db connection? */ /* Have we lost the db connection? */
if (pset->db == NULL && !pset->cur_cmd_interactive) { if (pset->db == NULL && !pset->cur_cmd_interactive)
{
successResult = EXIT_BADCONN; successResult = EXIT_BADCONN;
break; break;
} }
...@@ -365,4 +403,3 @@ MainLoop(PsqlSettings *pset, FILE *source) ...@@ -365,4 +403,3 @@ MainLoop(PsqlSettings *pset, FILE *source)
return successResult; return successResult;
} /* MainLoop() */ } /* MainLoop() */
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
#include "settings.h" #include "settings.h"
int int
MainLoop(PsqlSettings *pset, FILE *source); MainLoop(PsqlSettings *pset, FILE *source);
#endif MAINLOOP_H #endif /* MAINLOOP_H */
...@@ -28,13 +28,13 @@ ...@@ -28,13 +28,13 @@
static void static void
print_unaligned_text(const char * title, char ** headers, char ** cells, char ** footers, print_unaligned_text(const char *title, char **headers, char **cells, char **footers,
const char * opt_fieldsep, bool opt_barebones, const char *opt_fieldsep, bool opt_barebones,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int i; unsigned int i;
char ** ptr; char **ptr;
if (!opt_fieldsep) if (!opt_fieldsep)
opt_fieldsep = ""; opt_fieldsep = "";
...@@ -44,10 +44,12 @@ print_unaligned_text(const char * title, char ** headers, char ** cells, char ** ...@@ -44,10 +44,12 @@ print_unaligned_text(const char * title, char ** headers, char ** cells, char **
fprintf(fout, "%s\n", title); fprintf(fout, "%s\n", title);
/* print headers and count columns */ /* print headers and count columns */
for (ptr = headers; *ptr; ptr++) { for (ptr = headers; *ptr; ptr++)
{
col_count++; col_count++;
if (!opt_barebones) { if (!opt_barebones)
if (col_count>1) {
if (col_count > 1)
fputs(opt_fieldsep, fout); fputs(opt_fieldsep, fout);
fputs(*ptr, fout); fputs(*ptr, fout);
} }
...@@ -57,9 +59,10 @@ print_unaligned_text(const char * title, char ** headers, char ** cells, char ** ...@@ -57,9 +59,10 @@ print_unaligned_text(const char * title, char ** headers, char ** cells, char **
/* print cells */ /* print cells */
i = 0; i = 0;
for (ptr = cells; *ptr; ptr++) { for (ptr = cells; *ptr; ptr++)
{
fputs(*ptr, fout); fputs(*ptr, fout);
if ((i+1) % col_count) if ((i + 1) % col_count)
fputs(opt_fieldsep, fout); fputs(opt_fieldsep, fout);
else else
fputs("\n", fout); fputs("\n", fout);
...@@ -77,14 +80,14 @@ print_unaligned_text(const char * title, char ** headers, char ** cells, char ** ...@@ -77,14 +80,14 @@ print_unaligned_text(const char * title, char ** headers, char ** cells, char **
static void static void
print_unaligned_vertical(const char * title, char ** headers, char ** cells, char ** footers, print_unaligned_vertical(const char *title, char **headers, char **cells, char **footers,
const char * opt_fieldsep, bool opt_barebones, const char *opt_fieldsep, bool opt_barebones,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int i; unsigned int i;
unsigned int record = 1; unsigned int record = 1;
char ** ptr; char **ptr;
if (!opt_fieldsep) if (!opt_fieldsep)
opt_fieldsep = ""; opt_fieldsep = "";
...@@ -94,24 +97,26 @@ print_unaligned_vertical(const char * title, char ** headers, char ** cells, cha ...@@ -94,24 +97,26 @@ print_unaligned_vertical(const char * title, char ** headers, char ** cells, cha
fprintf(fout, "%s\n", title); fprintf(fout, "%s\n", title);
/* count columns */ /* count columns */
for (ptr = headers; *ptr; ptr++) { for (ptr = headers; *ptr; ptr++)
col_count++; col_count++;
}
/* print records */ /* print records */
for (i=0, ptr = cells; *ptr; i++, ptr++) { for (i = 0, ptr = cells; *ptr; i++, ptr++)
if (i % col_count == 0) { {
if (i % col_count == 0)
{
if (!opt_barebones) if (!opt_barebones)
fprintf(fout, "-- RECORD %d\n", record++); fprintf(fout, "-- RECORD %d\n", record++);
else else
fputc('\n', fout); fputc('\n', fout);
} }
fprintf(fout, "%s%s%s\n", headers[i%col_count], opt_fieldsep, *ptr); fprintf(fout, "%s%s%s\n", headers[i % col_count], opt_fieldsep, *ptr);
} }
/* print footers */ /* print footers */
if (!opt_barebones && footers) { if (!opt_barebones && footers)
{
fputs("--- END ---\n", fout); fputs("--- END ---\n", fout);
for (ptr = footers; *ptr; ptr++) for (ptr = footers; *ptr; ptr++)
fprintf(fout, "%s\n", *ptr); fprintf(fout, "%s\n", *ptr);
...@@ -127,19 +132,23 @@ print_unaligned_vertical(const char * title, char ** headers, char ** cells, cha ...@@ -127,19 +132,23 @@ print_unaligned_vertical(const char * title, char ** headers, char ** cells, cha
/* draw "line" */ /* draw "line" */
static void static void
_print_horizontal_line(const unsigned int col_count, const unsigned int * widths, unsigned short border, FILE * fout) _print_horizontal_line(const unsigned int col_count, const unsigned int *widths, unsigned short border, FILE *fout)
{ {
unsigned int i, j; unsigned int i,
j;
if (border == 1) if (border == 1)
fputc('-', fout); fputc('-', fout);
else if (border == 2) else if (border == 2)
fputs("+-", fout); fputs("+-", fout);
for (i=0; i<col_count; i++) { for (i = 0; i < col_count; i++)
for (j=0; j<widths[i]; j++) {
for (j = 0; j < widths[i]; j++)
fputc('-', fout); fputc('-', fout);
if (i<col_count-1) { if (i < col_count - 1)
{
if (border == 0) if (border == 0)
fputc(' ', fout); fputc(' ', fout);
else else
...@@ -158,77 +167,84 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int * widths ...@@ -158,77 +167,84 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int * widths
static void static void
print_aligned_text(const char * title, char ** headers, char ** cells, char ** footers, print_aligned_text(const char *title, char **headers, char **cells, char **footers,
const char * opt_align, bool opt_barebones, unsigned short int opt_border, const char *opt_align, bool opt_barebones, unsigned short int opt_border,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int i, tmp; unsigned int i,
unsigned int * widths, total_w; tmp;
char ** ptr; unsigned int *widths,
total_w;
char **ptr;
/* count columns */ /* count columns */
for (ptr = headers; *ptr; ptr++) for (ptr = headers; *ptr; ptr++)
col_count++; col_count++;
widths = calloc(col_count, sizeof (*widths)); widths = calloc(col_count, sizeof(*widths));
if (!widths) { if (!widths)
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* calc column widths */ /* calc column widths */
for (i=0; i<col_count; i++) for (i = 0; i < col_count; i++)
if ((tmp = strlen(headers[i])) > widths[i]) if ((tmp = strlen(headers[i])) > widths[i])
widths[i] = tmp; /* don't wanna call strlen twice */ widths[i] = tmp; /* don't wanna call strlen twice */
for (i=0, ptr = cells; *ptr; ptr++, i++) for (i = 0, ptr = cells; *ptr; ptr++, i++)
if ((tmp = strlen(*ptr)) > widths[i % col_count]) if ((tmp = strlen(*ptr)) > widths[i % col_count])
widths[i % col_count] = tmp; widths[i % col_count] = tmp;
if (opt_border==0) if (opt_border == 0)
total_w = col_count - 1; total_w = col_count - 1;
else if (opt_border==1) else if (opt_border == 1)
total_w = col_count*3 - 2; total_w = col_count * 3 - 2;
else else
total_w = col_count*3 + 1; total_w = col_count * 3 + 1;
for (i=0; i<col_count; i++) for (i = 0; i < col_count; i++)
total_w += widths[i]; total_w += widths[i];
/* print title */ /* print title */
if (title && !opt_barebones) { if (title && !opt_barebones)
if (strlen(title)>=total_w) {
if (strlen(title) >= total_w)
fprintf(fout, "%s\n", title); fprintf(fout, "%s\n", title);
else else
fprintf(fout, "%-*s%s\n", (total_w-strlen(title))/2, "", title); fprintf(fout, "%-*s%s\n", (total_w - strlen(title)) / 2, "", title);
} }
/* print headers */ /* print headers */
if (!opt_barebones) { if (!opt_barebones)
if (opt_border==2) {
if (opt_border == 2)
_print_horizontal_line(col_count, widths, opt_border, fout); _print_horizontal_line(col_count, widths, opt_border, fout);
if (opt_border==2) if (opt_border == 2)
fputs("| ", fout); fputs("| ", fout);
else if (opt_border==1) else if (opt_border == 1)
fputc(' ', fout); fputc(' ', fout);
for (i=0; i<col_count; i++) { for (i = 0; i < col_count; i++)
{
/* centered */ /* centered */
fprintf(fout, "%-*s%s%-*s", (int)floor((widths[i]-strlen(headers[i]))/2.0), "", headers[i], (int)ceil((widths[i]-strlen(headers[i]))/2.0) , ""); fprintf(fout, "%-*s%s%-*s", (int) floor((widths[i] - strlen(headers[i])) / 2.0), "", headers[i], (int) ceil((widths[i] - strlen(headers[i])) / 2.0), "");
if (i<col_count-1) { if (i < col_count - 1)
if (opt_border==0) {
if (opt_border == 0)
fputc(' ', fout); fputc(' ', fout);
else else
fputs(" | ", fout); fputs(" | ", fout);
} }
} }
if (opt_border==2) if (opt_border == 2)
fputs(" |", fout); fputs(" |", fout);
else if (opt_border==1) else if (opt_border == 1)
fputc(' ', fout);; fputc(' ', fout);;
fputc('\n', fout); fputc('\n', fout);
...@@ -236,41 +252,46 @@ print_aligned_text(const char * title, char ** headers, char ** cells, char ** f ...@@ -236,41 +252,46 @@ print_aligned_text(const char * title, char ** headers, char ** cells, char ** f
} }
/* print cells */ /* print cells */
for (i=0, ptr = cells; *ptr; i++, ptr++) { for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
/* beginning of line */ /* beginning of line */
if (i % col_count == 0) { if (i % col_count == 0)
if (opt_border==2) {
if (opt_border == 2)
fputs("| ", fout); fputs("| ", fout);
else if (opt_border==1) else if (opt_border == 1)
fputc(' ', fout); fputc(' ', fout);
} }
/* content */ /* content */
if (opt_align[(i) % col_count ] == 'r') if (opt_align[(i) % col_count] == 'r')
fprintf(fout, "%*s", widths[i%col_count], cells[i]); fprintf(fout, "%*s", widths[i % col_count], cells[i]);
else { else
if ((i+1) % col_count == 0 && opt_border != 2) {
if ((i + 1) % col_count == 0 && opt_border != 2)
fputs(cells[i], fout); fputs(cells[i], fout);
else else
fprintf(fout, "%-*s", widths[i%col_count], cells[i]); fprintf(fout, "%-*s", widths[i % col_count], cells[i]);
} }
/* divider */ /* divider */
if ((i+1) % col_count) { if ((i + 1) % col_count)
if (opt_border==0) {
if (opt_border == 0)
fputc(' ', fout); fputc(' ', fout);
else else
fputs(" | ", fout); fputs(" | ", fout);
} }
/* end of line */ /* end of line */
else { else
if (opt_border==2) {
if (opt_border == 2)
fputs(" |", fout); fputs(" |", fout);
fputc('\n', fout); fputc('\n', fout);
} }
} }
if (opt_border==2) if (opt_border == 2)
_print_horizontal_line(col_count, widths, opt_border, fout); _print_horizontal_line(col_count, widths, opt_border, fout);
/* print footers */ /* print footers */
...@@ -287,18 +308,22 @@ print_aligned_text(const char * title, char ** headers, char ** cells, char ** f ...@@ -287,18 +308,22 @@ print_aligned_text(const char * title, char ** headers, char ** cells, char ** f
static void static void
print_aligned_vertical(const char * title, char ** headers, char ** cells, char ** footers, print_aligned_vertical(const char *title, char **headers, char **cells, char **footers,
bool opt_barebones, unsigned short int opt_border, bool opt_barebones, unsigned short int opt_border,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int record = 1; unsigned int record = 1;
char ** ptr; char **ptr;
unsigned int i, tmp, hwidth=0, dwidth=0; unsigned int i,
char * divider; tmp,
hwidth = 0,
dwidth = 0;
char *divider;
/* count columns and find longest header */ /* count columns and find longest header */
for (ptr = headers; *ptr; ptr++) { for (ptr = headers; *ptr; ptr++)
{
col_count++; col_count++;
if ((tmp = strlen(*ptr)) > hwidth) if ((tmp = strlen(*ptr)) > hwidth)
hwidth = tmp; /* don't wanna call strlen twice */ hwidth = tmp; /* don't wanna call strlen twice */
...@@ -315,45 +340,55 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char ...@@ -315,45 +340,55 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char
/* make horizontal border */ /* make horizontal border */
divider = malloc(hwidth + dwidth + 10); divider = malloc(hwidth + dwidth + 10);
if (!divider) { if (!divider)
{
perror("malloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
divider[0] = '\0'; divider[0] = '\0';
if (opt_border == 2) if (opt_border == 2)
strcat(divider, "+-"); strcat(divider, "+-");
for (i=0; i<hwidth; i++) strcat(divider, opt_border > 0 ? "-" : " "); for (i = 0; i < hwidth; i++)
strcat(divider, opt_border > 0 ? "-" : " ");
if (opt_border > 0) if (opt_border > 0)
strcat(divider, "-+-"); strcat(divider, "-+-");
else else
strcat(divider, " "); strcat(divider, " ");
for (i=0; i<dwidth; i++) strcat(divider, opt_border > 0 ? "-" : " "); for (i = 0; i < dwidth; i++)
strcat(divider, opt_border > 0 ? "-" : " ");
if (opt_border == 2) if (opt_border == 2)
strcat(divider, "-+"); strcat(divider, "-+");
/* print records */ /* print records */
for (i=0, ptr = cells; *ptr; i++, ptr++) { for (i = 0, ptr = cells; *ptr; i++, ptr++)
if (i % col_count == 0) { {
if (!opt_barebones) { if (i % col_count == 0)
char * div_copy = strdup(divider); {
char * record_str = malloc(32); if (!opt_barebones)
{
char *div_copy = strdup(divider);
char *record_str = malloc(32);
size_t record_str_len; size_t record_str_len;
if (!div_copy || !record_str) { if (!div_copy || !record_str)
{
perror("malloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (opt_border==0) if (opt_border == 0)
sprintf(record_str, "* Record %d", record++); sprintf(record_str, "* Record %d", record++);
else else
sprintf(record_str, "[ RECORD %d ]", record++); sprintf(record_str, "[ RECORD %d ]", record++);
record_str_len = strlen(record_str); record_str_len = strlen(record_str);
if (record_str_len + opt_border > strlen(div_copy)) { if (record_str_len + opt_border > strlen(div_copy))
void * new; {
void *new;
new = realloc(div_copy, record_str_len + opt_border); new = realloc(div_copy, record_str_len + opt_border);
if (!new) { if (!new)
{
perror("realloc"); perror("realloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -369,7 +404,7 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char ...@@ -369,7 +404,7 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char
} }
if (opt_border == 2) if (opt_border == 2)
fputs("| ", fout); fputs("| ", fout);
fprintf(fout, "%-*s", hwidth, headers[i%col_count]); fprintf(fout, "%-*s", hwidth, headers[i % col_count]);
if (opt_border > 0) if (opt_border > 0)
fputs(" | ", fout); fputs(" | ", fout);
else else
...@@ -387,7 +422,8 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char ...@@ -387,7 +422,8 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char
/* print footers */ /* print footers */
if (!opt_barebones && footers && *footers) { if (!opt_barebones && footers && *footers)
{
if (opt_border < 2) if (opt_border < 2)
fputc('\n', fout); fputc('\n', fout);
for (ptr = footers; *ptr; ptr++) for (ptr = footers; *ptr; ptr++)
...@@ -408,11 +444,13 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char ...@@ -408,11 +444,13 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char
static void static void
html_escaped_print(const char * in, FILE * fout) html_escaped_print(const char *in, FILE *fout)
{ {
const char * p; const char *p;
for (p=in; *p; p++)
switch (*p) { for (p = in; *p; p++)
switch (*p)
{
case '&': case '&':
fputs("&amp;", fout); fputs("&amp;", fout);
break; break;
...@@ -433,14 +471,14 @@ html_escaped_print(const char * in, FILE * fout) ...@@ -433,14 +471,14 @@ html_escaped_print(const char * in, FILE * fout)
static void static void
print_html_text(const char * title, char ** headers, char ** cells, char ** footers, print_html_text(const char *title, char **headers, char **cells, char **footers,
const char * opt_align, bool opt_barebones, unsigned short int opt_border, const char *opt_align, bool opt_barebones, unsigned short int opt_border,
char * opt_table_attr, char *opt_table_attr,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int i; unsigned int i;
char ** ptr; char **ptr;
fprintf(fout, "<table border=%d", opt_border); fprintf(fout, "<table border=%d", opt_border);
if (opt_table_attr) if (opt_table_attr)
...@@ -448,7 +486,8 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot ...@@ -448,7 +486,8 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot
fputs(">\n", fout); fputs(">\n", fout);
/* print title */ /* print title */
if (!opt_barebones && title) { if (!opt_barebones && title)
{
fputs(" <caption>", fout); fputs(" <caption>", fout);
html_escaped_print(title, fout); html_escaped_print(title, fout);
fputs("</caption>\n", fout); fputs("</caption>\n", fout);
...@@ -457,9 +496,11 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot ...@@ -457,9 +496,11 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot
/* print headers and count columns */ /* print headers and count columns */
if (!opt_barebones) if (!opt_barebones)
fputs(" <tr>\n", fout); fputs(" <tr>\n", fout);
for (i=0, ptr = headers; *ptr; i++, ptr++) { for (i = 0, ptr = headers; *ptr; i++, ptr++)
{
col_count++; col_count++;
if (!opt_barebones) { if (!opt_barebones)
{
fputs(" <th align=center>", fout); fputs(" <th align=center>", fout);
html_escaped_print(*ptr, fout); html_escaped_print(*ptr, fout);
fputs("</th>\n", fout); fputs("</th>\n", fout);
...@@ -469,18 +510,20 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot ...@@ -469,18 +510,20 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot
fputs(" </tr>\n", fout); fputs(" </tr>\n", fout);
/* print cells */ /* print cells */
for (i=0, ptr = cells; *ptr; i++, ptr++) { for (i = 0, ptr = cells; *ptr; i++, ptr++)
if ( i % col_count == 0 ) {
if (i % col_count == 0)
fputs(" <tr valign=top>\n", fout); fputs(" <tr valign=top>\n", fout);
fprintf(fout, " <td align=%s>", opt_align[(i)%col_count] == 'r' ? "right" : "left"); fprintf(fout, " <td align=%s>", opt_align[(i) % col_count] == 'r' ? "right" : "left");
if ( (*ptr)[strspn(*ptr, " \t")] == '\0' ) /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only
* whitespace? */
fputs("&nbsp;", fout); fputs("&nbsp;", fout);
else else
html_escaped_print(*ptr, fout); html_escaped_print(*ptr, fout);
fputs("</td>\n", fout); fputs("</td>\n", fout);
if ( (i+1) % col_count == 0 ) if ((i + 1) % col_count == 0)
fputs(" </tr>\n", fout); fputs(" </tr>\n", fout);
} }
...@@ -489,7 +532,8 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot ...@@ -489,7 +532,8 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot
/* print footers */ /* print footers */
if (footers && !opt_barebones) if (footers && !opt_barebones)
for (ptr = footers; *ptr; ptr++) { for (ptr = footers; *ptr; ptr++)
{
html_escaped_print(*ptr, fout); html_escaped_print(*ptr, fout);
fputs("<br>\n", fout); fputs("<br>\n", fout);
} }
...@@ -500,15 +544,15 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot ...@@ -500,15 +544,15 @@ print_html_text(const char * title, char ** headers, char ** cells, char ** foot
static void static void
print_html_vertical(const char * title, char ** headers, char ** cells, char ** footers, print_html_vertical(const char *title, char **headers, char **cells, char **footers,
const char * opt_align, bool opt_barebones, unsigned short int opt_border, const char *opt_align, bool opt_barebones, unsigned short int opt_border,
char * opt_table_attr, char *opt_table_attr,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int i; unsigned int i;
unsigned int record = 1; unsigned int record = 1;
char ** ptr; char **ptr;
fprintf(fout, "<table border=%d", opt_border); fprintf(fout, "<table border=%d", opt_border);
if (opt_table_attr) if (opt_table_attr)
...@@ -516,7 +560,8 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -516,7 +560,8 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char **
fputs(">\n", fout); fputs(">\n", fout);
/* print title */ /* print title */
if (!opt_barebones && title) { if (!opt_barebones && title)
{
fputs(" <caption>", fout); fputs(" <caption>", fout);
html_escaped_print(title, fout); html_escaped_print(title, fout);
fputs("</caption>\n", fout); fputs("</caption>\n", fout);
...@@ -527,8 +572,10 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -527,8 +572,10 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char **
col_count++; col_count++;
/* print records */ /* print records */
for (i=0, ptr = cells; *ptr; i++, ptr++) { for (i = 0, ptr = cells; *ptr; i++, ptr++)
if (i % col_count == 0) { {
if (i % col_count == 0)
{
if (!opt_barebones) if (!opt_barebones)
fprintf(fout, "\n <tr><td colspan=2 align=center>Record %d</td></tr>\n", record++); fprintf(fout, "\n <tr><td colspan=2 align=center>Record %d</td></tr>\n", record++);
else else
...@@ -536,11 +583,12 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -536,11 +583,12 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char **
} }
fputs(" <tr valign=top>\n" fputs(" <tr valign=top>\n"
" <th>", fout); " <th>", fout);
html_escaped_print(headers[i%col_count], fout); html_escaped_print(headers[i % col_count], fout);
fputs("</th>\n", fout); fputs("</th>\n", fout);
fprintf(fout, " <td align=%s>", opt_align[i%col_count] == 'r' ? "right" : "left"); fprintf(fout, " <td align=%s>", opt_align[i % col_count] == 'r' ? "right" : "left");
if ( (*ptr)[strspn(*ptr, " \t")] == '\0' ) /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only
* whitespace? */
fputs("&nbsp;", fout); fputs("&nbsp;", fout);
else else
html_escaped_print(*ptr, fout); html_escaped_print(*ptr, fout);
...@@ -551,7 +599,8 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -551,7 +599,8 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char **
/* print footers */ /* print footers */
if (footers && !opt_barebones) if (footers && !opt_barebones)
for (ptr = footers; *ptr; ptr++) { for (ptr = footers; *ptr; ptr++)
{
html_escaped_print(*ptr, fout); html_escaped_print(*ptr, fout);
fputs("<br>\n", fout); fputs("<br>\n", fout);
} }
...@@ -567,11 +616,13 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -567,11 +616,13 @@ print_html_vertical(const char * title, char ** headers, char ** cells, char **
static void static void
latex_escaped_print(const char * in, FILE * fout) latex_escaped_print(const char *in, FILE *fout)
{ {
const char * p; const char *p;
for (p=in; *p; p++)
switch (*p) { for (p = in; *p; p++)
switch (*p)
{
case '&': case '&':
fputs("\\&", fout); fputs("\\&", fout);
break; break;
...@@ -601,18 +652,19 @@ latex_escaped_print(const char * in, FILE * fout) ...@@ -601,18 +652,19 @@ latex_escaped_print(const char * in, FILE * fout)
static void static void
print_latex_text(const char * title, char ** headers, char ** cells, char ** footers, print_latex_text(const char *title, char **headers, char **cells, char **footers,
const char * opt_align, bool opt_barebones, unsigned short int opt_border, const char *opt_align, bool opt_barebones, unsigned short int opt_border,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int i; unsigned int i;
const char * cp; const char *cp;
char ** ptr; char **ptr;
/* print title */ /* print title */
if (!opt_barebones && title) { if (!opt_barebones && title)
{
fputs("\begin{center}\n", fout); fputs("\begin{center}\n", fout);
latex_escaped_print(title, fout); latex_escaped_print(title, fout);
fputs("\n\end{center}\n\n", fout); fputs("\n\end{center}\n\n", fout);
...@@ -620,16 +672,21 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo ...@@ -620,16 +672,21 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo
/* begin environment and set alignments and borders */ /* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout); fputs("\\begin{tabular}{", fout);
if (opt_border==0) if (opt_border == 0)
fputs(opt_align, fout); fputs(opt_align, fout);
else if (opt_border==1) { else if (opt_border == 1)
for (cp = opt_align; *cp; cp++) { {
if (cp != opt_align) fputc('|', fout); for (cp = opt_align; *cp; cp++)
{
if (cp != opt_align)
fputc('|', fout);
fputc(*cp, fout); fputc(*cp, fout);
} }
} }
else if (opt_border==2) { else if (opt_border == 2)
for (cp = opt_align; *cp; cp++) { {
for (cp = opt_align; *cp; cp++)
{
fputc('|', fout); fputc('|', fout);
fputc(*cp, fout); fputc(*cp, fout);
} }
...@@ -637,35 +694,39 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo ...@@ -637,35 +694,39 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo
} }
fputs("}\n", fout); fputs("}\n", fout);
if (!opt_barebones && opt_border==2) if (!opt_barebones && opt_border == 2)
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
/* print headers and count columns */ /* print headers and count columns */
for (i=0, ptr = headers; *ptr; i++, ptr++) { for (i = 0, ptr = headers; *ptr; i++, ptr++)
{
col_count++; col_count++;
if (!opt_barebones) { if (!opt_barebones)
if (i!=0) {
if (i != 0)
fputs(" & ", fout); fputs(" & ", fout);
latex_escaped_print(*ptr, fout); latex_escaped_print(*ptr, fout);
} }
} }
if (!opt_barebones) { if (!opt_barebones)
{
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
} }
/* print cells */ /* print cells */
for (i=0, ptr = cells; *ptr; i++, ptr++) { for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
latex_escaped_print(*ptr, fout); latex_escaped_print(*ptr, fout);
if ( (i+1) % col_count == 0 ) if ((i + 1) % col_count == 0)
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
else else
fputs(" & ", fout); fputs(" & ", fout);
} }
if (opt_border==2) if (opt_border == 2)
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
fputs("\\end{tabular}\n\n", fout); fputs("\\end{tabular}\n\n", fout);
...@@ -674,7 +735,8 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo ...@@ -674,7 +735,8 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo
/* print footers */ /* print footers */
if (footers && !opt_barebones) if (footers && !opt_barebones)
for (ptr = footers; *ptr; ptr++) { for (ptr = footers; *ptr; ptr++)
{
latex_escaped_print(*ptr, fout); latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
} }
...@@ -685,19 +747,20 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo ...@@ -685,19 +747,20 @@ print_latex_text(const char * title, char ** headers, char ** cells, char ** foo
static void static void
print_latex_vertical(const char * title, char ** headers, char ** cells, char ** footers, print_latex_vertical(const char *title, char **headers, char **cells, char **footers,
const char * opt_align, bool opt_barebones, unsigned short int opt_border, const char *opt_align, bool opt_barebones, unsigned short int opt_border,
FILE * fout) FILE *fout)
{ {
unsigned int col_count = 0; unsigned int col_count = 0;
unsigned int i; unsigned int i;
char ** ptr; char **ptr;
unsigned int record = 1; unsigned int record = 1;
(void)opt_align; /* currently unused parameter */ (void) opt_align; /* currently unused parameter */
/* print title */ /* print title */
if (!opt_barebones && title) { if (!opt_barebones && title)
{
fputs("\begin{center}\n", fout); fputs("\begin{center}\n", fout);
latex_escaped_print(title, fout); latex_escaped_print(title, fout);
fputs("\n\end{center}\n\n", fout); fputs("\n\end{center}\n\n", fout);
...@@ -705,11 +768,11 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -705,11 +768,11 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char **
/* begin environment and set alignments and borders */ /* begin environment and set alignments and borders */
fputs("\\begin{tabular}{", fout); fputs("\\begin{tabular}{", fout);
if (opt_border==0) if (opt_border == 0)
fputs("cl", fout); fputs("cl", fout);
else if (opt_border==1) else if (opt_border == 1)
fputs("c|l", fout); fputs("c|l", fout);
else if (opt_border==2) else if (opt_border == 2)
fputs("|c|l|", fout); fputs("|c|l|", fout);
fputs("}\n", fout); fputs("}\n", fout);
...@@ -720,10 +783,13 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -720,10 +783,13 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char **
/* print records */ /* print records */
for (i=0, ptr = cells; *ptr; i++, ptr++) { for (i = 0, ptr = cells; *ptr; i++, ptr++)
{
/* new record */ /* new record */
if (i % col_count == 0) { if (i % col_count == 0)
if (!opt_barebones) { {
if (!opt_barebones)
{
if (opt_border == 2) if (opt_border == 2)
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
fprintf(fout, "\\multicolumn{2}{c}{Record %d} \\\\\n", record++); fprintf(fout, "\\multicolumn{2}{c}{Record %d} \\\\\n", record++);
...@@ -732,13 +798,13 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -732,13 +798,13 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char **
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
} }
latex_escaped_print(headers[i%col_count], fout); latex_escaped_print(headers[i % col_count], fout);
fputs(" & ", fout); fputs(" & ", fout);
latex_escaped_print(*ptr, fout); latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
} }
if (opt_border==2) if (opt_border == 2)
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
fputs("\\end{tabular}\n\n", fout); fputs("\\end{tabular}\n\n", fout);
...@@ -747,7 +813,8 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -747,7 +813,8 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char **
/* print footers */ /* print footers */
if (footers && !opt_barebones) if (footers && !opt_barebones)
for (ptr = footers; *ptr; ptr++) { for (ptr = footers; *ptr; ptr++)
{
latex_escaped_print(*ptr, fout); latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
} }
...@@ -766,14 +833,14 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char ** ...@@ -766,14 +833,14 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char **
void void
printTable(const char * title, char ** headers, char ** cells, char ** footers, printTable(const char *title, char **headers, char **cells, char **footers,
const char * align, const char *align,
const printTableOpt * opt, FILE * fout) const printTableOpt * opt, FILE *fout)
{ {
char * default_footer[] = { NULL }; char *default_footer[] = {NULL};
unsigned short int border = opt->border; unsigned short int border = opt->border;
FILE * pager = NULL, FILE *pager = NULL,
* output; *output;
if (opt->format == PRINT_NOTHING) if (opt->format == PRINT_NOTHING)
...@@ -795,41 +862,47 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers, ...@@ -795,41 +862,47 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers,
#endif #endif
) )
{ {
const char * pagerprog; const char *pagerprog;
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
unsigned int col_count=0, row_count=0, lines; unsigned int col_count = 0,
char ** ptr; row_count = 0,
lines;
char **ptr;
int result; int result;
struct winsize screen_size; struct winsize screen_size;
/* rough estimate of columns and rows */ /* rough estimate of columns and rows */
if (headers) if (headers)
for (ptr=headers; *ptr; ptr++) for (ptr = headers; *ptr; ptr++)
col_count++; col_count++;
if (cells) if (cells)
for (ptr=cells; *ptr; ptr++) for (ptr = cells; *ptr; ptr++)
row_count++; row_count++;
row_count /= col_count; row_count /= col_count;
if (opt->expanded) if (opt->expanded)
lines = (col_count+1) * row_count; lines = (col_count + 1) * row_count;
else else
lines = row_count+1; lines = row_count + 1;
if (!opt->tuples_only) if (!opt->tuples_only)
lines += 5; lines += 5;
result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size); result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
if (result==-1 || lines > screen_size.ws_row) { if (result == -1 || lines > screen_size.ws_row)
{
#endif #endif
pagerprog = getenv("PAGER"); pagerprog = getenv("PAGER");
if (!pagerprog) pagerprog = DEFAULT_PAGER; if (!pagerprog)
pagerprog = DEFAULT_PAGER;
pager = popen(pagerprog, "w"); pager = popen(pagerprog, "w");
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
} }
#endif #endif
} }
if (pager) { if (pager)
{
output = pager; output = pager;
pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN);
} }
...@@ -839,7 +912,8 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers, ...@@ -839,7 +912,8 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers,
/* print the stuff */ /* print the stuff */
switch (opt->format) { switch (opt->format)
{
case PRINT_UNALIGNED: case PRINT_UNALIGNED:
if (opt->expanded) if (opt->expanded)
print_unaligned_vertical(title, headers, cells, footers, opt->fieldSep, opt->tuples_only, output); print_unaligned_vertical(title, headers, cells, footers, opt->fieldSep, opt->tuples_only, output);
...@@ -868,7 +942,8 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers, ...@@ -868,7 +942,8 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers,
fprintf(stderr, "+ Oops, you shouldn't see this!\n"); fprintf(stderr, "+ Oops, you shouldn't see this!\n");
} }
if (pager) { if (pager)
{
pclose(pager); pclose(pager);
pqsignal(SIGPIPE, SIG_DFL); pqsignal(SIGPIPE, SIG_DFL);
} }
...@@ -877,37 +952,40 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers, ...@@ -877,37 +952,40 @@ printTable(const char * title, char ** headers, char ** cells, char ** footers,
void void
printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout) printQuery(PGresult *result, const printQueryOpt * opt, FILE *fout)
{ {
int nfields; int nfields;
char ** headers; char **headers;
char ** cells; char **cells;
char ** footers; char **footers;
char * align; char *align;
int i; int i;
/* extract headers */ /* extract headers */
nfields = PQnfields(result); nfields = PQnfields(result);
headers = calloc(nfields+1, sizeof(*headers)); headers = calloc(nfields + 1, sizeof(*headers));
if (!headers) { if (!headers)
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (i=0; i<nfields; i++) for (i = 0; i < nfields; i++)
headers[i] = PQfname(result, i); headers[i] = PQfname(result, i);
/* set cells */ /* set cells */
cells = calloc(nfields * PQntuples(result) + 1, sizeof(*cells)); cells = calloc(nfields * PQntuples(result) + 1, sizeof(*cells));
if (!cells) { if (!cells)
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (i=0; i< nfields * PQntuples(result); i++) { for (i = 0; i < nfields * PQntuples(result); i++)
{
if (PQgetisnull(result, i / nfields, i % nfields)) if (PQgetisnull(result, i / nfields, i % nfields))
cells[i] = opt->nullPrint ? opt->nullPrint : ""; cells[i] = opt->nullPrint ? opt->nullPrint : "";
else else
...@@ -918,15 +996,17 @@ printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout) ...@@ -918,15 +996,17 @@ printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout)
if (opt->footers) if (opt->footers)
footers = opt->footers; footers = opt->footers;
else if (!opt->topt.expanded) { else if (!opt->topt.expanded)
{
footers = calloc(2, sizeof(*footers)); footers = calloc(2, sizeof(*footers));
if (!footers) { if (!footers)
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
footers[0] = malloc(100); footers[0] = malloc(100);
if (PQntuples(result)==1) if (PQntuples(result) == 1)
strcpy(footers[0], "(1 row)"); strcpy(footers[0], "(1 row)");
else else
sprintf(footers[0], "(%d rows)", PQntuples(result)); sprintf(footers[0], "(%d rows)", PQntuples(result));
...@@ -936,18 +1016,21 @@ printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout) ...@@ -936,18 +1016,21 @@ printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout)
/* set alignment */ /* set alignment */
align = calloc(nfields+1, sizeof(*align)); align = calloc(nfields + 1, sizeof(*align));
if (!align) { if (!align)
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
for (i=0; i<nfields; i++) { for (i = 0; i < nfields; i++)
{
Oid ftype = PQftype(result, i); Oid ftype = PQftype(result, i);
if ( ftype == 20 || /* int8 */
if (ftype == 20 || /* int8 */
ftype == 21 || /* int2 */ ftype == 21 || /* int2 */
ftype == 23 || /* int4 */ ftype == 23 || /* int4 */
(ftype >=26 && ftype <=30) || /* ?id */ (ftype >= 26 && ftype <= 30) || /* ?id */
ftype == 700 || /* float4 */ ftype == 700 || /* float4 */
ftype == 701 || /* float8 */ ftype == 701 || /* float8 */
ftype == 790 || /* money */ ftype == 790 || /* money */
...@@ -965,7 +1048,8 @@ printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout) ...@@ -965,7 +1048,8 @@ printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout)
free(headers); free(headers);
free(cells); free(cells);
if (footers) { if (footers)
{
free(footers[0]); free(footers[0]);
free(footers); free(footers);
} }
......
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
#include <stdio.h> #include <stdio.h>
#include <libpq-fe.h> #include <libpq-fe.h>
enum printFormat { enum printFormat
{
PRINT_NOTHING = 0, /* to make sure someone initializes this */ PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_UNALIGNED, PRINT_UNALIGNED,
PRINT_ALIGNED, PRINT_ALIGNED,
...@@ -17,12 +18,16 @@ enum printFormat { ...@@ -17,12 +18,16 @@ enum printFormat {
}; };
typedef struct _printTableOpt { typedef struct _printTableOpt
{
enum printFormat format; /* one of the above */ enum printFormat format; /* one of the above */
bool expanded; /* expanded/vertical output (if supported by output format) */ bool expanded; /* expanded/vertical output (if supported
bool pager; /* use pager for output (if to stdout and stdout is a tty) */ * by output format) */
bool pager; /* use pager for output (if to stdout and
* stdout is a tty) */
bool tuples_only; /* don't output headers, row counts, etc. */ bool tuples_only; /* don't output headers, row counts, etc. */
unsigned short int border; /* Print a border around the table. 0=none, 1=dividing lines, 2=full */ unsigned short int border; /* Print a border around the table.
* 0=none, 1=dividing lines, 2=full */
char *fieldSep; /* field separator for unaligned text mode */ char *fieldSep; /* field separator for unaligned text mode */
char *tableAttr; /* attributes for HTML <table ...> */ char *tableAttr; /* attributes for HTML <table ...> */
} printTableOpt; } printTableOpt;
...@@ -39,19 +44,20 @@ typedef struct _printTableOpt { ...@@ -39,19 +44,20 @@ typedef struct _printTableOpt {
* - align is an 'l' or an 'r' for every column, if the output format needs it. * - align is an 'l' or an 'r' for every column, if the output format needs it.
* (You must specify this long enough. Otherwise anything could happen.) * (You must specify this long enough. Otherwise anything could happen.)
*/ */
void void printTable(const char *title, char **headers, char **cells, char **footers,
printTable(const char * title, char ** headers, char ** cells, char ** footers, const char *align,
const char * align, const printTableOpt * opt, FILE *fout);
const printTableOpt * opt, FILE * fout);
typedef struct _printQueryOpt { typedef struct _printQueryOpt
{
printTableOpt topt; /* the options above */ printTableOpt topt; /* the options above */
char * nullPrint; /* how to print null entities */ char *nullPrint; /* how to print null entities */
bool quote; /* quote all values as much as possible */ bool quote; /* quote all values as much as possible */
char * title; /* override title */ char *title; /* override title */
char ** footers; /* override footer (default is "(xx rows)") */ char **footers; /* override footer (default is "(xx
* rows)") */
} printQueryOpt; } printQueryOpt;
/* /*
...@@ -60,7 +66,7 @@ typedef struct _printQueryOpt { ...@@ -60,7 +66,7 @@ typedef struct _printQueryOpt {
* It calls the printTable above with all the things set straight. * It calls the printTable above with all the things set straight.
*/ */
void void
printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout); printQuery(PGresult *result, const printQueryOpt * opt, FILE *fout);
#endif /* PRINT_H */ #endif /* PRINT_H */
...@@ -60,11 +60,11 @@ const char * ...@@ -60,11 +60,11 @@ const char *
get_prompt(PsqlSettings *pset, promptStatus_t status) get_prompt(PsqlSettings *pset, promptStatus_t status)
{ {
#define MAX_PROMPT_SIZE 256 #define MAX_PROMPT_SIZE 256
static char destination[MAX_PROMPT_SIZE+1]; static char destination[MAX_PROMPT_SIZE + 1];
char buf[MAX_PROMPT_SIZE+1]; char buf[MAX_PROMPT_SIZE + 1];
bool esc = false; bool esc = false;
const char *p; const char *p;
const char * prompt_string; const char *prompt_string;
if (GetVariable(pset->vars, "quiet")) if (GetVariable(pset->vars, "quiet"))
return ""; return "";
...@@ -82,10 +82,10 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -82,10 +82,10 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
destination[0] = '\0'; destination[0] = '\0';
for (p = prompt_string; for (p = prompt_string;
p && *p && strlen(destination)<MAX_PROMPT_SIZE; p && *p && strlen(destination) < MAX_PROMPT_SIZE;
p++) p++)
{ {
MemSet(buf, 0, MAX_PROMPT_SIZE+1); MemSet(buf, 0, MAX_PROMPT_SIZE + 1);
if (esc) if (esc)
{ {
switch (*p) switch (*p)
...@@ -99,11 +99,14 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -99,11 +99,14 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
if (pset->db) if (pset->db)
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE); strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
break; break;
case '~': { case '~':
const char * var; {
if (pset->db) { const char *var;
if ( strcmp(PQdb(pset->db), PQuser(pset->db))==0 ||
( (var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset->db))==0) ) if (pset->db)
{
if (strcmp(PQdb(pset->db), PQuser(pset->db)) == 0 ||
((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset->db)) == 0))
strcpy(buf, "~"); strcpy(buf, "~");
else else
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE); strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
...@@ -113,11 +116,13 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -113,11 +116,13 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
/* DB server hostname (long/short) */ /* DB server hostname (long/short) */
case 'M': case 'M':
case 'm': case 'm':
if (pset->db) { if (pset->db)
if (PQhost(pset->db)) { {
if (PQhost(pset->db))
{
strncpy(buf, PQhost(pset->db), MAX_PROMPT_SIZE); strncpy(buf, PQhost(pset->db), MAX_PROMPT_SIZE);
if (*p == 'm') if (*p == 'm')
buf[strcspn(buf,".")] = '\0'; buf[strcspn(buf, ".")] = '\0';
} }
else else
buf[0] = '.'; buf[0] = '.';
...@@ -125,7 +130,8 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -125,7 +130,8 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
break; break;
/* DB server port number */ /* DB server port number */
case '>': case '>':
if (pset->db) { if (pset->db)
{
if (PQhost(pset->db)) if (PQhost(pset->db))
strncpy(buf, PQport(pset->db), MAX_PROMPT_SIZE); strncpy(buf, PQport(pset->db), MAX_PROMPT_SIZE);
else else
...@@ -138,19 +144,29 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -138,19 +144,29 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
strncpy(buf, PQuser(pset->db), MAX_PROMPT_SIZE); strncpy(buf, PQuser(pset->db), MAX_PROMPT_SIZE);
break; break;
case '0': case '1': case '2': case '3': case '4': case '0':
case '5': case '6': case '7': case '8': case '9': case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{ {
long int l; long int l;
char * end; char *end;
l = strtol(p, &end, 0); l = strtol(p, &end, 0);
sprintf(buf, "%c", (unsigned char)l); sprintf(buf, "%c", (unsigned char) l);
p = end-1; p = end - 1;
break; break;
} }
case 'R': case 'R':
switch(status) { switch (status)
{
case PROMPT_READY: case PROMPT_READY:
if (!pset->db) if (!pset->db)
buf[0] = '!'; buf[0] = '!';
...@@ -182,7 +198,7 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -182,7 +198,7 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
case '#': case '#':
{ {
if (pset->db && strcmp(PQuser(pset->db), "postgres")==0) if (pset->db && strcmp(PQuser(pset->db), "postgres") == 0)
buf[0] = '#'; buf[0] = '#';
else else
buf[0] = '>'; buf[0] = '>';
...@@ -193,21 +209,23 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -193,21 +209,23 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
/* execute command */ /* execute command */
case '`': case '`':
{ {
FILE * fd = NULL; FILE *fd = NULL;
char * file = strdup(p+1); char *file = strdup(p + 1);
int cmdend; int cmdend;
cmdend = strcspn(file, "`"); cmdend = strcspn(file, "`");
file[cmdend] = '\0'; file[cmdend] = '\0';
if (file) if (file)
fd = popen(file, "r"); fd = popen(file, "r");
if (fd) { if (fd)
fgets(buf, MAX_PROMPT_SIZE-1, fd); {
fgets(buf, MAX_PROMPT_SIZE - 1, fd);
pclose(fd); pclose(fd);
} }
if (buf[strlen(buf)-1] == '\n') if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf)-1] = '\0'; buf[strlen(buf) - 1] = '\0';
free(file); free(file);
p += cmdend+1; p += cmdend + 1;
break; break;
} }
...@@ -217,14 +235,15 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -217,14 +235,15 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
char *name; char *name;
const char *val; const char *val;
int nameend; int nameend;
name = strdup(p+1);
name = strdup(p + 1);
nameend = strcspn(name, "$"); nameend = strcspn(name, "$");
name[nameend] = '\0'; name[nameend] = '\0';
val = interpolate_var(name, pset); val = interpolate_var(name, pset);
if (val) if (val)
strncpy(buf, val, MAX_PROMPT_SIZE); strncpy(buf, val, MAX_PROMPT_SIZE);
free(name); free(name);
p += nameend+1; p += nameend + 1;
break; break;
} }
...@@ -245,12 +264,10 @@ get_prompt(PsqlSettings *pset, promptStatus_t status) ...@@ -245,12 +264,10 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
esc = false; esc = false;
} }
if (!esc) { if (!esc)
strncat(destination, buf, MAX_PROMPT_SIZE-strlen(destination)); strncat(destination, buf, MAX_PROMPT_SIZE - strlen(destination));
}
} }
destination[MAX_PROMPT_SIZE] = '\0'; destination[MAX_PROMPT_SIZE] = '\0';
return destination; return destination;
} }
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
#include "settings.h" #include "settings.h"
typedef enum _promptStatus { typedef enum _promptStatus
{
PROMPT_READY, PROMPT_READY,
PROMPT_CONTINUE, PROMPT_CONTINUE,
PROMPT_COMMENT, PROMPT_COMMENT,
...@@ -13,7 +14,7 @@ typedef enum _promptStatus { ...@@ -13,7 +14,7 @@ typedef enum _promptStatus {
} promptStatus_t; } promptStatus_t;
const char * const char *
get_prompt(PsqlSettings *pset, promptStatus_t status); get_prompt(PsqlSettings *pset, promptStatus_t status);
#endif /* PROMPT_H */ #endif /* PROMPT_H */
/*-------------------------------------------------------------------------
*
* psql.c
* an interactive front-end to postgreSQL
*
* Copyright (c) 1996, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.195 1999/10/26 04:40:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#else
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include "postgres.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "pqsignal.h"
#include "stringutils.h"
#include "psqlHelp.h"
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_LIBREADLINE
#ifdef HAVE_READLINE_H
#include <readline.h>
#define USE_READLINE 1
#if defined(HAVE_HISTORY_H)
#include <history.h>
#define USE_HISTORY 1
#endif
#else
#if defined(HAVE_READLINE_READLINE_H)
#include <readline/readline.h>
#define USE_READLINE 1
#if defined(HAVE_READLINE_HISTORY_H)
#include <readline/history.h>
#define USE_HISTORY 1
#endif
#endif
#endif
#if defined(HAVE_HISTORY) && !defined(USE_HISTORY)
#define USE_HISTORY 1
#endif
#endif
#ifdef WIN32
#define popen(x,y) _popen(x,y)
#define pclose(x) _pclose(x)
#define open(x,y,z) _open(x,y,z)
#define strcasecmp(x,y) stricmp(x,y)
#define pqsignal(x,y)
#define R_OK 0
/* getopt is not in the standard includes on Win32 */
extern char *optarg;
extern int optind,
opterr,
optopt;
int getopt(int, char *const[], const char *);
char *__progname = "psql";
#endif
#ifdef MULTIBYTE
/* flag to indicate if PGCLIENTENCODING has been set by a user */
static char *has_client_encoding = 0;
#endif
/* This prompt string is assumed to have at least 3 characters by code in MainLoop().
* A character two characters from the end is replaced each time by a mode character.
*/
#define PROMPT "=> "
#define PROMPT_READY '='
#define PROMPT_CONTINUE '-'
#define PROMPT_COMMENT '*'
#define PROMPT_SINGLEQUOTE '\''
#define PROMPT_DOUBLEQUOTE '"'
/* Backslash command handling:
* 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 (i.e. we got a \q)
* 3 - new query supplied by edit
*/
#define CMD_UNKNOWN -1
#define CMD_SEND 0
#define CMD_SKIP_LINE 1
#define CMD_TERMINATE 2
#define CMD_NEWEDIT 3
#define COPYBUFSIZ 8192
#define DEFAULT_FIELD_SEP "|"
#define DEFAULT_EDITOR "vi"
#define DEFAULT_SHELL "/bin/sh"
typedef struct _psqlSettings
{
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() */
bool echoQuery; /* echo the query before sending it */
bool echoAllQueries; /* echo all queries before sending it */
bool quiet; /* run quietly, no messages, no promt */
bool singleStep; /* prompt before for each query */
bool singleLineMode; /* query terminated by newline */
bool useReadline; /* use libreadline routines */
bool getPassword; /* prompt the user for a username and
* password */
} PsqlSettings;
/*
* cur_cmd_source and cur_cmd_interactive are the top of a stack of
* source files (one stack level per recursive invocation of MainLoop).
* It's kinda grotty to make these global variables, but the alternative
* of passing them around through many function parameter lists seems
* worse.
*/
static FILE *cur_cmd_source = NULL; /* current source of command input */
static bool cur_cmd_interactive = false; /* is it an interactive
* source? */
#ifdef TIOCGWINSZ
struct winsize screen_size;
#else
struct winsize
{
int ws_row;
int ws_col;
} screen_size;
#endif
/* declarations for functions in this file */
static void usage(char *progname);
static void slashUsage();
static bool handleCopyOut(PGconn *conn, FILE *copystream);
static bool handleCopyIn(PGconn *conn, const bool mustprompt,
FILE *copystream);
static int tableList(PsqlSettings *pset, bool deep_tablelist,
char info_type, bool system_tables);
static int tableDesc(PsqlSettings *pset, char *table, FILE *fout);
static int objectDescription(PsqlSettings *pset, char *object);
static int rightsList(PsqlSettings *pset);
static void emitNtimes(FILE *fout, const char *str, int N);
static void prompt_for_password(char *username, char *password);
static char *gets_noreadline(char *prompt, FILE *source);
static char *gets_readline(char *prompt, FILE *source);
static char *gets_fromFile(char *prompt, FILE *source);
static int listAllDbs(PsqlSettings *pset);
static bool SendQuery(PsqlSettings *pset, const char *query,
FILE *copy_in_stream, FILE *copy_out_stream);
static int HandleSlashCmds(PsqlSettings *pset, char *line,
PQExpBuffer query_buf);
static int MainLoop(PsqlSettings *pset, FILE *source);
static FILE *setFout(PsqlSettings *pset, char *fname);
static char *selectVersion(PsqlSettings *pset);
/*
* usage print out usage for command line arguments
*/
static void
usage(char *progname)
{
fprintf(stderr, "Usage: %s [options] [dbname]\n", progname);
fprintf(stderr, "\t -a authsvc set authentication service\n");
fprintf(stderr, "\t -A turn off alignment when printing out attributes\n");
fprintf(stderr, "\t -c query run single query (slash commands too)\n");
fprintf(stderr, "\t -d dbName specify database name\n");
fprintf(stderr, "\t -e echo the query sent to the backend\n");
fprintf(stderr, "\t -E echo all queries sent to the backend\n");
fprintf(stderr, "\t -f filename use file as a source of queries\n");
fprintf(stderr, "\t -F sep set the field separator (default is '|')\n");
fprintf(stderr, "\t -h host set database server host\n");
fprintf(stderr, "\t -H turn on html3.0 table output\n");
fprintf(stderr, "\t -l list available databases\n");
fprintf(stderr, "\t -n don't use readline library\n");
fprintf(stderr, "\t -o filename send output to filename or (|pipe)\n");
fprintf(stderr, "\t -p port set port number\n");
fprintf(stderr, "\t -q run quietly (no messages, no prompts)\n");
fprintf(stderr, "\t -s single step mode (prompts for each query)\n");
fprintf(stderr, "\t -S single line mode (i.e. query terminated by newline)\n");
fprintf(stderr, "\t -t turn off printing of headings and row count\n");
fprintf(stderr, "\t -T html set html3.0 table command options (cf. -H)\n");
fprintf(stderr, "\t -u ask for a username and password for authentication\n");
fprintf(stderr, "\t -x turn on expanded output (field names on left)\n");
exit(1);
}
/*
* slashUsage print out usage for the backslash commands
*/
static char *
on(bool f)
{
return f ? "on" : "off";
}
static void
slashUsage(PsqlSettings *pset)
{
int usePipe = 0;
char *pagerenv;
FILE *fout;
#ifdef TIOCGWINSZ
if (pset->notty == 0 &&
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
screen_size.ws_col == 0 ||
screen_size.ws_row == 0))
{
#endif
screen_size.ws_row = 24;
screen_size.ws_col = 80;
#ifdef TIOCGWINSZ
}
#endif
if (pset->notty == 0 &&
(pagerenv = getenv("PAGER")) &&
(pagerenv[0] != '\0') &&
screen_size.ws_row <= 35 &&
(fout = popen(pagerenv, "w")))
{
usePipe = 1;
pqsignal(SIGPIPE, SIG_IGN);
}
else
fout = stdout;
/* if you add/remove a line here, change the row test above */
fprintf(fout, " \\? -- help\n");
fprintf(fout, " \\a -- toggle field-alignment (currently %s)\n", on(pset->opt.align));
fprintf(fout, " \\C [<captn>] -- set html3 caption (currently '%s')\n", pset->opt.caption ? pset->opt.caption : "");
fprintf(fout, " \\connect <dbname|-> <user> -- connect to new database (currently '%s')\n", PQdb(pset->db));
fprintf(fout, " \\copy table {from | to} <fname>\n");
fprintf(fout, " \\d [<table>] -- list tables and indices, columns in <table>, or * for all\n");
fprintf(fout, " \\da -- list aggregates\n");
fprintf(fout, " \\dd [<object>]- list comment an object.\n");
fprintf(fout, " \\df -- list functions\n");
fprintf(fout, " \\di -- list only indices\n");
fprintf(fout, " \\do -- list operators\n");
fprintf(fout, " \\ds -- list only sequences\n");
fprintf(fout, " \\dS -- list system tables and indexes\n");
fprintf(fout, " \\dt -- list only tables\n");
fprintf(fout, " \\dT -- list types\n");
fprintf(fout, " \\e [<fname>] -- edit the current query buffer or <fname>\n");
fprintf(fout, " \\E [<fname>] -- edit the current query buffer or <fname>, and execute\n");
fprintf(fout, " \\f [<sep>] -- change field separater (currently '%s')\n", pset->opt.fieldSep);
fprintf(fout, " \\g [<fname>] [|<cmd>] -- send query to backend [and results in <fname> or pipe]\n");
fprintf(fout, " \\h [<cmd>] -- help on syntax of sql commands, * for all commands\n");
fprintf(fout, " \\H -- toggle html3 output (currently %s)\n", on(pset->opt.html3));
fprintf(fout, " \\i <fname> -- read and execute queries from filename\n");
fprintf(fout, " \\l -- list all databases\n");
fprintf(fout, " \\m -- toggle monitor-like table display (currently %s)\n", on(pset->opt.standard));
fprintf(fout, " \\o [<fname>] [|<cmd>] -- send all query results to stdout, <fname>, or pipe\n");
fprintf(fout, " \\p -- print the current query buffer\n");
fprintf(fout, " \\q -- quit\n");
fprintf(fout, " \\r -- reset(clear) the query buffer\n");
fprintf(fout, " \\s [<fname>] -- print history or save it in <fname>\n");
fprintf(fout, " \\t -- toggle table headings and row count (currently %s)\n", on(pset->opt.header));
fprintf(fout, " \\T [<html>] -- set html3.0 <table ...> options (currently '%s')\n", pset->opt.tableOpt ? pset->opt.tableOpt : "");
fprintf(fout, " \\x -- toggle expanded output (currently %s)\n", on(pset->opt.expanded));
fprintf(fout, " \\w <fname> -- write current buffer to a file\n");
fprintf(fout, " \\z -- list current grant/revoke permissions\n");
fprintf(fout, " \\! [<cmd>] -- shell escape or command\n");
if (usePipe)
{
pclose(fout);
pqsignal(SIGPIPE, SIG_DFL);
}
}
static PGresult *
PSQLexec(PsqlSettings *pset, char *query)
{
PGresult *res;
if (pset->echoAllQueries)
{
fprintf(stderr, "QUERY: %s\n", query);
fprintf(stderr, "\n");
fflush(stderr);
}
res = PQexec(pset->db, query);
if (!res)
fputs(PQerrorMessage(pset->db), stderr);
else
{
if (PQresultStatus(res) == PGRES_COMMAND_OK ||
PQresultStatus(res) == PGRES_TUPLES_OK)
return res;
if (!pset->quiet)
fputs(PQerrorMessage(pset->db), stderr);
PQclear(res);
}
return NULL;
}
/*
* Code to support command cancellation.
* If interactive, we enable a SIGINT signal catcher that sends
* a cancel request to the backend.
* Note that sending the cancel directly from the signal handler
* is safe only because PQrequestCancel is carefully written to
* make it so. We have to be very careful what else we do in the
* signal handler.
* Writing on stderr is potentially dangerous, if the signal interrupted
* some stdio operation on stderr. On Unix we can avoid trouble by using
* write() instead; on Windows that's probably not workable, but we can
* at least avoid trusting printf by using the more primitive fputs.
*/
static PGconn *cancelConn = NULL; /* connection to try cancel on */
static void
safe_write_stderr(const char *s)
{
#ifdef WIN32
fputs(s, stderr);
#else
write(fileno(stderr), s, strlen(s));
#endif
}
static void
handle_sigint(SIGNAL_ARGS)
{
if (cancelConn == NULL)
exit(1); /* accept signal if no connection */
/* Try to send cancel request */
if (PQrequestCancel(cancelConn))
safe_write_stderr("\nCANCEL request sent\n");
else
{
safe_write_stderr("\nCannot send cancel request:\n");
safe_write_stderr(PQerrorMessage(cancelConn));
}
}
/*
* listAllDbs
*
* list all the databases in the system returns 0 if all went well
*
*
*/
static int
listAllDbs(PsqlSettings *pset)
{
PGresult *results;
char *query = "select * from pg_database;";
if (!(results = PSQLexec(pset, query)))
return 1;
else
{
PQprint(pset->queryFout,
results,
&pset->opt);
PQclear(results);
return 0;
}
}
/*
* List The Database Tables returns 0 if all went well
*
*/
static int
tableList(PsqlSettings *pset, bool deep_tablelist, char info_type,
bool system_tables)
{
char listbuf[512];
int nColumns;
int i;
char *rk;
char *rr;
PGresult *res;
int usePipe = 0;
bool haveIndexes = false;
char *pagerenv;
FILE *fout;
#ifdef TIOCGWINSZ
if (pset->notty == 0 &&
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
screen_size.ws_col == 0 ||
screen_size.ws_row == 0))
{
#endif
screen_size.ws_row = 24;
screen_size.ws_col = 80;
#ifdef TIOCGWINSZ
}
#endif
listbuf[0] = '\0';
strcat(listbuf, "SELECT usename, relname, relkind, relhasrules ");
strcat(listbuf, "FROM pg_class, pg_user ");
strcat(listbuf, "WHERE usesysid = relowner ");
switch (info_type)
{
case 't':
strcat(listbuf, "and ( relkind = 'r') ");
break;
case 'i':
strcat(listbuf, "and ( relkind = 'i') ");
haveIndexes = true;
break;
case 'S':
strcat(listbuf, "and ( relkind = 'S') ");
break;
case 'b':
default:
strcat(listbuf, "and ( relkind = 'r' OR relkind = 'i' OR relkind = 'S') ");
haveIndexes = true;
break;
}
if (!system_tables)
strcat(listbuf, "and relname !~ '^pg_' ");
else
strcat(listbuf, "and relname ~ '^pg_' ");
/*
* Large-object relations are automatically ignored because they have
* relkind 'l'. However, we want to ignore their indexes as well.
* The clean way to do that would be to do a join to find out which
* table each index is for. The ugly but fast way is to know that
* large object indexes have names starting with 'xinx'.
*/
if (haveIndexes)
strcat(listbuf, "and (relkind != 'i' OR relname !~ '^xinx') ");
strcat(listbuf, " ORDER BY relname ");
if (!(res = PSQLexec(pset, listbuf)))
return -1;
/* first, print out the attribute names */
nColumns = PQntuples(res);
if (nColumns > 0)
{
if (pset->notty == 0 &&
(pagerenv = getenv("PAGER")) &&
pagerenv[0] != '\0' &&
(deep_tablelist ||
screen_size.ws_row <= nColumns + 7) &&
(fout = popen(pagerenv, "w")))
{
usePipe = 1;
pqsignal(SIGPIPE, SIG_IGN);
}
else
fout = stdout;
if (deep_tablelist)
{
/* describe everything here */
char **table;
table = (char **) malloc(nColumns * sizeof(char *));
if (table == NULL)
perror("malloc");
/* load table table */
/*
* Put double quotes around the table name to allow for
* mixed-case and whitespaces in the table name. - BGA
* 1998-11-14
*/
for (i = 0; i < nColumns; i++)
{
table[i] = (char *) malloc(PQgetlength(res, i, 1) * sizeof(char) + 3);
if (table[i] == NULL)
perror("malloc");
strcpy(table[i], "\"");
strcat(table[i], PQgetvalue(res, i, 1));
strcat(table[i], "\"");
}
PQclear(res);
for (i = 0; i < nColumns; i++)
tableDesc(pset, table[i], fout);
free(table);
}
else
{
/* Display the information */
fprintf(fout, "Database = %s\n", PQdb(pset->db));
fprintf(fout, " +------------------+----------------------------------+----------+\n");
fprintf(fout, " | Owner | Relation | Type |\n");
fprintf(fout, " +------------------+----------------------------------+----------+\n");
/* next, print out the instances */
for (i = 0; i < PQntuples(res); i++)
{
fprintf(fout, " | %-16.16s", PQgetvalue(res, i, 0));
fprintf(fout, " | %-32.32s | ", PQgetvalue(res, i, 1));
rk = PQgetvalue(res, i, 2);
rr = PQgetvalue(res, i, 3);
if (strcmp(rk, "r") == 0)
fprintf(fout, "%-8.8s |", (rr[0] == 't') ? "view?" : "table");
else if (strcmp(rk, "i") == 0)
fprintf(fout, "%-8.8s |", "index");
else
fprintf(fout, "%-8.8s |", "sequence");
fprintf(fout, "\n");
}
fprintf(fout, " +------------------+----------------------------------+----------+\n");
fprintf(fout, "\n");
PQclear(res);
}
if (usePipe)
{
pclose(fout);
pqsignal(SIGPIPE, SIG_DFL);
}
return 0;
}
else
{
PQclear(res);
switch (info_type)
{
case 't':
fprintf(stderr, "Couldn't find any tables!\n");
break;
case 'i':
fprintf(stderr, "Couldn't find any indices!\n");
break;
case 'S':
fprintf(stderr, "Couldn't find any sequences!\n");
break;
case 'b':
default:
fprintf(stderr, "Couldn't find any tables, sequences or indices!\n");
break;
}
return -1;
}
}
/*
* List Tables Grant/Revoke Permissions returns 0 if all went well
*
*/
static int
rightsList(PsqlSettings *pset)
{
char listbuf[512];
int nColumns;
int i;
int maxCol1Len;
int maxCol2Len;
int usePipe = 0;
char *pagerenv;
FILE *fout;
PGresult *res;
#ifdef TIOCGWINSZ
if (pset->notty == 0 &&
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
screen_size.ws_col == 0 ||
screen_size.ws_row == 0))
{
#endif
screen_size.ws_row = 24;
screen_size.ws_col = 80;
#ifdef TIOCGWINSZ
}
#endif
listbuf[0] = '\0';
strcat(listbuf, "SELECT relname, relacl ");
strcat(listbuf, "FROM pg_class ");
/* Currently, we ignore indexes since they have no meaningful rights */
strcat(listbuf, "WHERE ( relkind = 'r' OR relkind = 'S') ");
strcat(listbuf, " and relname !~ '^pg_'");
strcat(listbuf, " ORDER BY relname ");
if (!(res = PSQLexec(pset, listbuf)))
return -1;
/* first, print out the attribute names */
nColumns = PQntuples(res);
if (nColumns > 0)
{
if (pset->notty == 0 &&
(pagerenv = getenv("PAGER")) &&
pagerenv[0] != '\0' &&
screen_size.ws_row <= nColumns + 7 &&
(fout = popen(pagerenv, "w")))
{
usePipe = 1;
pqsignal(SIGPIPE, SIG_IGN);
}
else
fout = stdout;
/* choose column widths */
maxCol1Len = strlen("Relation");
maxCol2Len = strlen("Grant/Revoke Permissions");
for (i = 0; i < PQntuples(res); i++)
{
int l = strlen(PQgetvalue(res, i, 0));
if (l > maxCol1Len)
maxCol1Len = l;
l = strlen(PQgetvalue(res, i, 1));
if (l > maxCol2Len)
maxCol2Len = l;
}
/* Display the information */
fprintf(fout, "Database = %s\n", PQdb(pset->db));
fprintf(fout, " +");
emitNtimes(fout, "-", maxCol1Len + 2);
fprintf(fout, "+");
emitNtimes(fout, "-", maxCol2Len + 2);
fprintf(fout, "+\n");
fprintf(fout, " | %-*s | %-*s |\n",
maxCol1Len, "Relation",
maxCol2Len, "Grant/Revoke Permissions");
fprintf(fout, " +");
emitNtimes(fout, "-", maxCol1Len + 2);
fprintf(fout, "+");
emitNtimes(fout, "-", maxCol2Len + 2);
fprintf(fout, "+\n");
/* next, print out the instances */
for (i = 0; i < PQntuples(res); i++)
{
fprintf(fout, " | %-*s | %-*s |\n",
maxCol1Len, PQgetvalue(res, i, 0),
maxCol2Len, PQgetvalue(res, i, 1));
}
fprintf(fout, " +");
emitNtimes(fout, "-", maxCol1Len + 2);
fprintf(fout, "+");
emitNtimes(fout, "-", maxCol2Len + 2);
fprintf(fout, "+\n");
PQclear(res);
if (usePipe)
{
pclose(fout);
pqsignal(SIGPIPE, SIG_DFL);
}
return 0;
}
else
{
PQclear(res);
fprintf(stderr, "Couldn't find any tables!\n");
return -1;
}
}
static void
emitNtimes(FILE *fout, const char *str, int N)
{
int i;
for (i = 0; i < N; i++)
fputs(str, fout);
}
/*
* Describe a table
*
* Describe the columns in a database table. returns 0 if all went well
*
*
*/
static int
tableDesc(PsqlSettings *pset, char *table, FILE *fout)
{
char descbuf[512];
int nColumns,
nIndices;
char *rtype;
char *rnotnull;
char *rhasdef;
int i;
int attlen,
atttypmod;
PGresult *res,
*res2;
int usePipe = 0;
char *pagerenv;
#ifdef TIOCGWINSZ
if (pset->notty == 0 &&
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
screen_size.ws_col == 0 ||
screen_size.ws_row == 0))
{
#endif
screen_size.ws_row = 24;
screen_size.ws_col = 80;
#ifdef TIOCGWINSZ
}
#endif
/* Build the query */
/*
* if the table name is surrounded by double-quotes, then don't
* convert case
*/
if (*table == '"')
{
table++;
if (*(table + strlen(table) - 1) == '"')
*(table + strlen(table) - 1) = '\0';
}
else
{
#ifdef MULTIBYTE
for (i = 0; table[i]; i += PQmblen(table + i))
#else
for (i = 0; table[i]; i++)
#endif
if (isascii((unsigned char) table[i]) &&
isupper(table[i]))
table[i] = tolower(table[i]);
}
descbuf[0] = '\0';
strcat(descbuf, "SELECT a.attnum, a.attname, t.typname, a.attlen, ");
strcat(descbuf, "a.atttypmod, a.attnotnull, a.atthasdef ");
strcat(descbuf, "FROM pg_class c, pg_attribute a, pg_type t ");
strcat(descbuf, "WHERE c.relname = '");
strcat(descbuf, table);
strcat(descbuf, "'");
strcat(descbuf, " and a.attnum > 0 ");
strcat(descbuf, " and a.attrelid = c.oid ");
strcat(descbuf, " and a.atttypid = t.oid ");
strcat(descbuf, " ORDER BY attnum ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
/* first, print out the attribute names */
nColumns = PQntuples(res);
if (nColumns > 0)
{
if (fout == NULL)
{
if (pset->notty == 0 &&
(pagerenv = getenv("PAGER")) &&
pagerenv[0] != '\0' &&
screen_size.ws_row <= nColumns + 7 &&
(fout = popen(pagerenv, "w")))
{
usePipe = 1;
pqsignal(SIGPIPE, SIG_IGN);
}
else
fout = stdout;
}
/*
* Extract the veiw name and veiw definition from pg_views. -Ryan
* 2/14/99
*/
descbuf[0] = '\0';
strcat(descbuf, "SELECT viewname, definition ");
strcat(descbuf, "FROM pg_views ");
strcat(descbuf, "WHERE viewname like '");
strcat(descbuf, table);
strcat(descbuf, "' ");
if (!(res2 = PSQLexec(pset, descbuf)))
return -1;
/*
* Display the information
*/
if (PQntuples(res2))
{
/*
* display the query. o * -Ryan 2/14/99
*/
fprintf(fout, "View = %s\n", table);
fprintf(fout, "Query = %s\n", PQgetvalue(res2, 0, 1));
}
else
fprintf(fout, "Table = %s\n", table);
PQclear(res2);
fprintf(fout, "+----------------------------------+----------------------------------+-------+\n");
fprintf(fout, "| Field | Type | Length|\n");
fprintf(fout, "+----------------------------------+----------------------------------+-------+\n");
/* next, print out the instances */
for (i = 0; i < PQntuples(res); i++)
{
char type_str[33];
fprintf(fout, "| %-32.32s | ", PQgetvalue(res, i, 1));
rtype = PQgetvalue(res, i, 2);
attlen = atoi(PQgetvalue(res, i, 3));
atttypmod = atoi(PQgetvalue(res, i, 4));
rnotnull = PQgetvalue(res, i, 5);
rhasdef = PQgetvalue(res, i, 6);
strcpy(type_str, rtype);
if (strcmp(rtype, "bpchar") == 0)
strcpy(type_str, "char()");
else if (strcmp(rtype, "varchar") == 0)
strcpy(type_str, "varchar()");
else if (rtype[0] == '_')
{
strcpy(type_str, rtype + 1);
strncat(type_str, "[]", 32 - strlen(type_str));
type_str[32] = '\0';
}
if (rnotnull[0] == 't')
{
strncat(type_str, " not null", 32 - strlen(type_str));
type_str[32] = '\0';
}
if (rhasdef[0] == 't')
{
descbuf[0] = '\0';
strcat(descbuf, "SELECT d.adsrc ");
strcat(descbuf, "FROM pg_attrdef d, pg_class c ");
strcat(descbuf, "WHERE c.relname = '");
strcat(descbuf, table);
strcat(descbuf, "'");
strcat(descbuf, " and c.oid = d.adrelid ");
strcat(descbuf, " and d.adnum = ");
strcat(descbuf, PQgetvalue(res, i, 0));
if (!(res2 = PSQLexec(pset, descbuf)))
return -1;
strcat(type_str, " default ");
strncat(type_str, PQgetvalue(res2, 0, 0), 32 - strlen(type_str));
type_str[32] = '\0';
}
fprintf(fout, "%-32.32s |", type_str);
if (strcmp(rtype, "text") == 0)
fprintf(fout, "%6s |", "var");
else if (strcmp(rtype, "bpchar") == 0 ||
strcmp(rtype, "varchar") == 0)
fprintf(fout, "%6i |", atttypmod != -1 ? atttypmod - VARHDRSZ : 0);
else if (strcmp(rtype, "numeric") == 0)
fprintf(fout, "%3i.%-2i |",
((atttypmod - VARHDRSZ) >> 16) & 0xffff,
(atttypmod - VARHDRSZ) & 0xffff);
else
{
if (attlen > 0)
fprintf(fout, "%6i |", attlen);
else
fprintf(fout, "%6s |", "var");
}
fprintf(fout, "\n");
}
fprintf(fout, "+----------------------------------+----------------------------------+-------+\n");
PQclear(res);
/* display defined indexes for this table */
descbuf[0] = '\0';
strcat(descbuf, "SELECT c2.relname ");
strcat(descbuf, "FROM pg_class c, pg_class c2, pg_index i ");
strcat(descbuf, "WHERE c.relname = '");
strcat(descbuf, table);
strcat(descbuf, "'");
strcat(descbuf, " and c.oid = i.indrelid ");
strcat(descbuf, " and i.indexrelid = c2.oid ");
strcat(descbuf, " ORDER BY c2.relname ");
if ((res = PSQLexec(pset, descbuf)))
{
nIndices = PQntuples(res);
if (nIndices > 0)
{
/*
* Display the information
*/
if (nIndices == 1)
fprintf(fout, "Index: ");
else
fprintf(fout, "Indices: ");
/* next, print out the instances */
for (i = 0; i < PQntuples(res); i++)
if (i == 0)
fprintf(fout, "%s\n", PQgetvalue(res, i, 0));
else
fprintf(fout, " %s\n", PQgetvalue(res, i, 0));
fprintf(fout, "\n");
}
PQclear(res);
}
if (usePipe)
{
pclose(fout);
pqsignal(SIGPIPE, SIG_DFL);
}
return 0;
}
else
{
PQclear(res);
fprintf(stderr, "Couldn't find table %s!\n", table);
return -1;
}
}
/*
* Get object comments
*
* Describe the columns in a database table. returns 0 if all went well
*
*
*/
static int
objectDescription(PsqlSettings *pset, char *object)
{
char descbuf[512];
PGresult *res;
int i;
bool success;
/* Build the query */
while (isspace(*object))
object++;
/*
* if the object name is surrounded by double-quotes, then don't
* convert case
*/
if (*object == '"')
{
object++;
if (*(object + strlen(object) - 1) == '"')
*(object + strlen(object) - 1) = '\0';
}
else
{
#ifdef MULTIBYTE
for (i = 0; object[i]; i += PQmblen(object + i))
#else
for (i = 0; object[i]; i++)
#endif
if (isupper(object[i]))
object[i] = tolower(object[i]);
}
descbuf[0] = '\0';
if (strchr(object, '.') != NULL)
{
char table[NAMEDATALEN],
column[NAMEDATALEN];
StrNCpy(table, object,
((strchr(object, '.') - object + 1) < NAMEDATALEN) ?
(strchr(object, '.') - object + 1) : NAMEDATALEN);
StrNCpy(column, strchr(object, '.') + 1, NAMEDATALEN);
strcat(descbuf, "SELECT DISTINCT description ");
strcat(descbuf, "FROM pg_class, pg_attribute, pg_description ");
strcat(descbuf, "WHERE pg_class.relname = '");
strcat(descbuf, table);
strcat(descbuf, "' and ");
strcat(descbuf, "pg_class.oid = pg_attribute.attrelid and ");
strcat(descbuf, "pg_attribute.attname = '");
strcat(descbuf, column);
strcat(descbuf, "' and ");
strcat(descbuf, " pg_attribute.oid = pg_description.objoid ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
}
else
{
strcat(descbuf, "SELECT DISTINCT description ");
strcat(descbuf, "FROM pg_class, pg_description ");
strcat(descbuf, "WHERE pg_class.relname ~ '^");
strcat(descbuf, object);
strcat(descbuf, "'");
strcat(descbuf, " and pg_class.oid = pg_description.objoid ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
else if (PQntuples(res) <= 0)
{
PQclear(res);
descbuf[0] = '\0';
strcat(descbuf, "SELECT DISTINCT description ");
strcat(descbuf, "FROM pg_type, pg_description ");
strcat(descbuf, "WHERE pg_type.typname ~ '^");
strcat(descbuf, object);
strcat(descbuf, "' and ");
strcat(descbuf, " pg_type.oid = pg_description.objoid ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
else if (PQntuples(res) <= 0)
{
PQclear(res);
descbuf[0] = '\0';
strcat(descbuf, "SELECT DISTINCT description ");
strcat(descbuf, "FROM pg_proc, pg_description ");
strcat(descbuf, "WHERE pg_proc.proname ~ '^");
strcat(descbuf, object);
strcat(descbuf, "'");
strcat(descbuf, " and pg_proc.oid = pg_description.objoid ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
else if (PQntuples(res) <= 0)
{
PQclear(res);
descbuf[0] = '\0';
strcat(descbuf, "SELECT DISTINCT description ");
strcat(descbuf, "FROM pg_operator, pg_description ");
strcat(descbuf, "WHERE pg_operator.oprname ~ '^");
strcat(descbuf, object);
strcat(descbuf, "'");
/* operator descriptions are attached to the proc */
strcat(descbuf, " and RegprocToOid(pg_operator.oprcode) = pg_description.objoid ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
else if (PQntuples(res) <= 0)
{
PQclear(res);
descbuf[0] = '\0';
strcat(descbuf, "SELECT DISTINCT description ");
strcat(descbuf, "FROM pg_aggregate, pg_description ");
strcat(descbuf, "WHERE pg_aggregate.aggname ~ '^");
strcat(descbuf, object);
strcat(descbuf, "'");
strcat(descbuf, " and pg_aggregate.oid = pg_description.objoid ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
else if (PQntuples(res) <= 0)
{
PQclear(res);
descbuf[0] = '\0';
strcat(descbuf, "SELECT 'no description' as description ");
if (!(res = PSQLexec(pset, descbuf)))
return -1;
}
}
}
}
}
}
PQclear(res);
success = SendQuery(pset, descbuf, NULL, NULL);
return 0;
}
/*
* Basic routines to read a line of input
*
* All three routines will return a malloc'd string of indefinite size.
*/
typedef char *(*READ_ROUTINE) (char *prompt, FILE *source);
/*
* gets_noreadline
* gets a line of interactive input (without using readline)
*
* the source is ignored
*/
static char *
gets_noreadline(char *prompt, FILE *source)
{
fputs(prompt, stdout);
fflush(stdout);
return gets_fromFile(prompt, stdin);
}
/*
* gets_readline
* gets a line of interactive input using readline library
*
* the source is ignored
*/
static char *
gets_readline(char *prompt, FILE *source)
{
char *s;
#ifdef USE_READLINE
s = readline(prompt);
#else
s = gets_noreadline(prompt, source);
#endif
fputc('\r', stdout);
fflush(stdout);
return s;
}
/*
* gets_fromFile
* gets a line of noninteractive input from a file
*
* the prompt is ignored
*/
static char *
gets_fromFile(char *prompt, FILE *source)
{
PQExpBufferData buffer;
char line[COPYBUFSIZ];
initPQExpBuffer(&buffer);
while (fgets(line, COPYBUFSIZ, source) != NULL)
{
appendPQExpBufferStr(&buffer, line);
if (buffer.data[buffer.len-1] == '\n')
return buffer.data;
}
if (buffer.len > 0)
return buffer.data; /* EOF after reading some bufferload(s) */
/* EOF, so return null */
termPQExpBuffer(&buffer);
return NULL;
}
/*
* SendQuery: send the query string to the backend.
*
* Return true if the query executed successfully, false otherwise.
*
* If not NULL, copy_in_stream and copy_out_stream are files to redirect
* copy in/out data to.
*/
static bool
SendQuery(PsqlSettings *pset, const char *query,
FILE *copy_in_stream, FILE *copy_out_stream)
{
bool success = false;
PGresult *results;
PGnotify *notify;
if (pset->singleStep)
fprintf(stdout, "\n**************************************"
"*****************************************\n");
if (pset->echoQuery || pset->singleStep)
{
fprintf(stderr, "QUERY: %s\n", query);
fflush(stderr);
}
if (pset->singleStep)
{
fprintf(stdout, "\n**************************************"
"*****************************************\n");
fflush(stdout);
printf("\npress return to continue ..\n");
gets_fromFile("", stdin);
}
results = PQexec(pset->db, query);
if (results == NULL)
{
fprintf(stderr, "%s", PQerrorMessage(pset->db));
success = false;
}
else
{
switch (PQresultStatus(results))
{
case PGRES_TUPLES_OK:
if (pset->gfname)
{
PsqlSettings settings_copy = *pset;
FILE *fp;
settings_copy.queryFout = stdout;
fp = setFout(&settings_copy, pset->gfname);
if (!fp || fp == stdout)
{
success = false;
break;
}
PQprint(fp,
results,
&pset->opt);
if (settings_copy.pipe)
pclose(fp);
else
fclose(fp);
free(pset->gfname);
pset->gfname = NULL;
success = true;
break;
}
else
{
success = true;
PQprint(pset->queryFout,
results,
&(pset->opt));
fflush(pset->queryFout);
}
break;
case PGRES_EMPTY_QUERY:
success = true;
break;
case PGRES_COMMAND_OK:
success = true;
if (!pset->quiet)
printf("%s\n", PQcmdStatus(results));
break;
case PGRES_COPY_OUT:
if (copy_out_stream)
success = handleCopyOut(pset->db, copy_out_stream);
else
{
if (pset->queryFout == stdout && !pset->quiet)
printf("Copy command returns...\n");
success = handleCopyOut(pset->db, pset->queryFout);
}
break;
case PGRES_COPY_IN:
if (copy_in_stream)
success = handleCopyIn(pset->db, false, copy_in_stream);
else
success = handleCopyIn(pset->db,
cur_cmd_interactive && !pset->quiet,
cur_cmd_source);
break;
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE:
success = false;
fprintf(stderr, "%s", PQerrorMessage(pset->db));
break;
}
if (PQstatus(pset->db) == CONNECTION_BAD)
{
fprintf(stderr,
"We have lost the connection to the backend, so "
"further processing is impossible. "
"Terminating.\n");
exit(2); /* we are out'ta here */
}
/* check for asynchronous returns */
while ((notify = PQnotifies(pset->db)) != NULL)
{
fprintf(stderr,
"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
notify->relname, notify->be_pid);
free(notify);
}
if (results)
PQclear(results);
}
return success;
}
static void
editFile(char *fname)
{
char *editorName;
char *sys;
editorName = getenv("EDITOR");
if (!editorName)
editorName = DEFAULT_EDITOR;
sys = malloc(strlen(editorName) + strlen(fname) + 32 + 1);
if (!sys)
{
perror("malloc");
exit(1);
}
sprintf(sys, "exec '%s' '%s'", editorName, fname);
system(sys);
free(sys);
}
static bool
toggle(PsqlSettings *pset, bool *sw, char *msg)
{
*sw = !*sw;
if (!pset->quiet)
printf("turned %s %s\n", on(*sw), msg);
return *sw;
}
static void
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 */
}
static 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;
}
}
}
}
}
}
static void
do_copy(const char *args, PsqlSettings *pset)
{
/*---------------------------------------------------------------------------
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[MAXPGPATH];
/* The pathname of the file from/to which we copy */
char table[NAMEDATALEN];
/* 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)
#ifndef __CYGWIN32__
copystream = fopen(file, "r");
#else
copystream = fopen(file, "rb");
#endif
else
#ifndef __CYGWIN32__
copystream = fopen(file, "w");
#else
copystream = fopen(file, "wb");
#endif
if (copystream == NULL)
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 */
success = SendQuery(pset, query,
from ? copystream : (FILE *) NULL,
!from ? copystream : (FILE *) NULL);
fclose(copystream);
if (!pset->quiet)
{
if (success)
printf("Successfully copied.\n");
else
printf("Copy failed.\n");
}
}
}
}
static void
do_connect(const char *new_dbname,
const char *new_user,
PsqlSettings *pset)
{
if (!new_dbname)
fprintf(stderr, "\\connect must be followed by a database name\n");
else if (new_user != NULL && pset->getPassword)
fprintf(stderr, "You can't specify a username when using passwords.\n");
else
{
PGconn *olddb = pset->db;
const char *dbparam;
const char *userparam;
const char *pwparam;
if (strcmp(new_dbname, "-") != 0)
dbparam = new_dbname;
else
dbparam = PQdb(olddb);
if (new_user != NULL && strcmp(new_user, "-") != 0)
userparam = new_user;
else
userparam = PQuser(olddb);
/* FIXME: if changing user, ought to prompt for a new password? */
pwparam = PQpass(olddb);
#ifdef MULTIBYTE
/*
* PGCLIENTENCODING may be set by the previous connection. if a
* user does not explicitly set PGCLIENTENCODING, we should
* discard PGCLIENTENCODING so that libpq could get the backend
* encoding as the default PGCLIENTENCODING value. -- 1998/12/12
* Tatsuo Ishii
*/
if (!has_client_encoding)
{
static const char ev[] = "PGCLIENTENCODING=";
putenv(ev);
}
#endif
pset->db = PQsetdbLogin(PQhost(olddb), PQport(olddb),
NULL, NULL, dbparam, userparam, pwparam);
if (!pset->quiet)
{
if (!new_user)
printf("connecting to new database: %s\n", dbparam);
else if (dbparam != new_dbname)
printf("connecting as new user: %s\n", new_user);
else
printf("connecting to new database: %s as user: %s\n",
dbparam, new_user);
}
if (PQstatus(pset->db) == CONNECTION_BAD)
{
fprintf(stderr, "%s\n", PQerrorMessage(pset->db));
fprintf(stderr, "Could not connect to new database. exiting\n");
exit(2);
}
else
{
cancelConn = pset->db; /* redirect sigint's loving
* attentions */
PQfinish(olddb);
free(pset->prompt);
pset->prompt = malloc(strlen(PQdb(pset->db)) + 10);
sprintf(pset->prompt, "%s%s", PQdb(pset->db), PROMPT);
}
}
}
static void
do_edit(const char *filename_arg, PQExpBuffer query_buf, int *status_p)
{
int fd;
char fnametmp[64];
char *fname;
int cc;
int ql = query_buf->len;
bool error;
char line[COPYBUFSIZ+1];
if (filename_arg)
{
fname = (char *) filename_arg;
error = false;
}
else
{
#ifndef WIN32
sprintf(fnametmp, "/tmp/psql.%ld.%ld",
(long) geteuid(), (long) getpid());
#else
GetTempFileName(".", "psql", 0, fnametmp);
#endif
fname = fnametmp;
unlink(fname);
if ((fd = open(fname, O_EXCL | O_CREAT | O_WRONLY, 0600)) < 0)
{
perror(fname);
error = true;
}
else
{
if (ql == 0 || query_buf->data[ql - 1] != '\n')
{
appendPQExpBufferChar(query_buf, '\n');
ql++;
}
if (write(fd, query_buf->data, ql) != ql)
{
perror(fname);
close(fd);
unlink(fname);
error = true;
}
else
{
close(fd);
error = false;
}
}
}
if (error)
*status_p = CMD_SKIP_LINE;
else
{
editFile(fname);
if ((fd = open(fname, O_RDONLY, 0)) < 0)
{
perror(fname);
*status_p = CMD_SKIP_LINE;
}
else
{
resetPQExpBuffer(query_buf);
while ((cc = (int) read(fd, line, COPYBUFSIZ)) > 0)
{
line[cc] = '\0';
appendPQExpBufferStr(query_buf, line);
}
close(fd);
rightTrim(query_buf->data);
query_buf->len = strlen(query_buf->data);
*status_p = CMD_NEWEDIT;
}
if (!filename_arg)
unlink(fname);
}
}
static void
do_help(PsqlSettings *pset, 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 */
int usePipe = 0;
char *pagerenv;
FILE *fout;
if (strcmp(topic, "*") == 0 &&
(pset->notty == 0) &&
(pagerenv = getenv("PAGER")) &&
(pagerenv[0] != '\0') &&
(fout = popen(pagerenv, "w")))
{
usePipe = 1;
pqsignal(SIGPIPE, SIG_IGN);
}
else
fout = stdout;
help_found = false; /* Haven't found it yet */
for (i = 0; QL_HELP[i].cmd; i++)
{
if (strcasecmp(QL_HELP[i].cmd, topic) == 0 ||
strcmp(topic, "*") == 0)
{
help_found = true;
fprintf(fout, "Command: %s\n", QL_HELP[i].cmd);
fprintf(fout, "Description: %s\n", QL_HELP[i].help);
fprintf(fout, "Syntax:\n");
fprintf(fout, "%s\n", QL_HELP[i].syntax);
fprintf(fout, "\n");
}
}
if (usePipe)
{
pclose(fout);
pqsignal(SIGPIPE, SIG_DFL);
}
if (!help_found)
fprintf(stderr, "command not found, "
"try \\h with no arguments to see available help\n");
}
}
static 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:
*
* Handles all the different commands that start with \
*
* 'line' is the current input line (ie, the backslash command)
* 'query_buf' contains the query-so-far, which may be modified by
* execution of the backslash command (for example, \r clears it)
*
* query_buf can be NULL if there is no query-so-far.
*
* Returns a status code:
* 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 (i.e. we got a \q)
* 3 - new query supplied by edit
*----------
*/
static int
HandleSlashCmds(PsqlSettings *pset,
char *line,
PQExpBuffer query_buf)
{
int status = CMD_SKIP_LINE;
bool success;
char *cmd;
/*
* String: value of the slash command, less the slash and with escape
* sequences decoded.
*/
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.
*/
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 */
if (strlen(cmd) >= 1 && cmd[strlen(cmd) - 1] == ';') /* strip trailing ; */
cmd[strlen(cmd) - 1] = '\0';
/*
* 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'".
*/
if (strlen(cmd) > 1)
optarg = cmd + 1 + strspn(cmd + 1, " \t");
else
optarg = NULL;
blank_loc = strcspn(cmd, " \t");
if (blank_loc == 0 || !cmd[blank_loc])
optarg2 = NULL;
else
optarg2 = cmd + blank_loc + strspn(cmd + blank_loc, " \t");
switch (cmd[0])
{
case 'a': /* toggles to align fields on output */
toggle(pset, &pset->opt.align, "field alignment");
break;
case 'C': /* define new caption */
if (pset->opt.caption)
{
free(pset->opt.caption);
pset->opt.caption = NULL;
}
if (optarg && !(pset->opt.caption = strdup(optarg)))
{
perror("malloc");
exit(CMD_TERMINATE);
}
break;
case 'c':
{
if (strncmp(cmd, "copy ", strlen("copy ")) == 0 ||
strncmp(cmd, "copy ", strlen("copy ")) == 0)
do_copy(optarg2, pset);
else if (strcmp(cmd, "copy") == 0)
{
fprintf(stderr, "See \\? for help\n");
break;
}
else if (strncmp(cmd, "connect ", strlen("connect ")) == 0 ||
strcmp(cmd, "connect") == 0 /* issue error message */ )
{
char *optarg3 = NULL;
int blank_loc2;
if (optarg2)
{
blank_loc2 = strcspn(optarg2, " \t");
if (blank_loc2 == 0 || *(optarg2 + blank_loc2) == '\0')
optarg3 = NULL;
else
{
optarg3 = optarg2 + blank_loc2 +
strspn(optarg2 + blank_loc2, " \t");
*(optarg2 + blank_loc2) = '\0';
}
}
do_connect(optarg2, optarg3, pset);
}
else
{
char *optarg3 = NULL;
int blank_loc2;
if (optarg)
{
blank_loc2 = strcspn(optarg, " \t");
if (blank_loc2 == 0 || *(optarg + blank_loc2) == '\0')
optarg3 = NULL;
else
{
optarg3 = optarg + blank_loc2 +
strspn(optarg + blank_loc2, " \t");
*(optarg + blank_loc2) = '\0';
}
}
do_connect(optarg, optarg3, pset);
}
}
break;
case 'd': /* \d describe database information */
/*
* if the optarg2 name is surrounded by double-quotes, then
* don't convert case
*/
if (optarg2)
{
if (*optarg2 == '"')
{
optarg2++;
if (*(optarg2 + strlen(optarg2) - 1) == '"')
*(optarg2 + strlen(optarg2) - 1) = '\0';
}
else
{
int i;
#ifdef MULTIBYTE
for (i = 0; optarg2[i]; i += PQmblen(optarg2 + i))
#else
for (i = 0; optarg2[i]; i++)
#endif
if (isupper(optarg2[i]))
optarg2[i] = tolower(optarg2[i]);
}
}
#ifdef TIOCGWINSZ
if (pset->notty == 0 &&
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
screen_size.ws_col == 0 ||
screen_size.ws_row == 0))
{
#endif
screen_size.ws_row = 24;
screen_size.ws_col = 80;
#ifdef TIOCGWINSZ
}
#endif
if (strncmp(cmd, "da", 2) == 0)
{
char descbuf[4096];
/* aggregates */
descbuf[0] = '\0';
strcat(descbuf, "SELECT a.aggname AS aggname, ");
strcat(descbuf, " t.typname AS type, ");
strcat(descbuf, " obj_description(a.oid) as description ");
strcat(descbuf, "FROM pg_aggregate a, pg_type t ");
strcat(descbuf, "WHERE a.aggbasetype = t.oid ");
if (optarg2)
{
strcat(descbuf, "AND a.aggname ~ '^");
strcat(descbuf, optarg2);
strcat(descbuf, "' ");
}
strcat(descbuf, "UNION ");
strcat(descbuf, "SELECT a.aggname AS aggname, ");
strcat(descbuf, " 'all types' as type, ");
strcat(descbuf, " obj_description(a.oid) as description ");
strcat(descbuf, "FROM pg_aggregate a ");
strcat(descbuf, "WHERE a.aggbasetype = 0 ");
if (optarg2)
{
strcat(descbuf, "AND a.aggname ~ '^");
strcat(descbuf, optarg2);
strcat(descbuf, "' ");
}
strcat(descbuf, "ORDER BY aggname, type;");
success = SendQuery(pset, descbuf, NULL, NULL);
}
else if (strncmp(cmd, "dd", 2) == 0)
/* descriptions */
objectDescription(pset, optarg + 1);
else if (strncmp(cmd, "df", 2) == 0)
{
char descbuf[4096];
/* functions/procedures */
/*
* we skip in/out funcs by excluding functions that take
* some arguments, but have no types defined for those
* arguments
*/
descbuf[0] = '\0';
strcat(descbuf, "SELECT t.typname as result, ");
strcat(descbuf, " p.proname as function, ");
if (screen_size.ws_col <= 80)
strcat(descbuf, " substr(oid8types(p.proargtypes),1,14) as arguments, ");
else
strcat(descbuf, " oid8types(p.proargtypes) as arguments, ");
if (screen_size.ws_col <= 80)
strcat(descbuf, " substr(obj_description(p.oid),1,34) as description ");
else
strcat(descbuf, " obj_description(p.oid) as description ");
strcat(descbuf, "FROM pg_proc p, pg_type t ");
strcat(descbuf, "WHERE p.prorettype = t.oid and ");
strcat(descbuf, "(pronargs = 0 or oid8types(p.proargtypes) != '') ");
if (optarg2)
{
strcat(descbuf, "AND p.proname ~ '^");
strcat(descbuf, optarg2);
strcat(descbuf, "' ");
}
strcat(descbuf, "ORDER BY result, function, arguments;");
success = SendQuery(pset, descbuf, NULL, NULL);
}
else if (strncmp(cmd, "di", 2) == 0)
/* only indices */
tableList(pset, false, 'i', false);
else if (strncmp(cmd, "do", 2) == 0)
{
char descbuf[4096];
/* operators */
descbuf[0] = '\0';
strcat(descbuf, "SELECT o.oprname AS op, ");
strcat(descbuf, " t1.typname AS left_arg, ");
strcat(descbuf, " t2.typname AS right_arg, ");
strcat(descbuf, " t0.typname AS result, ");
if (screen_size.ws_col <= 80)
strcat(descbuf, " substr(obj_description(p.oid),1,41) as description ");
else
strcat(descbuf, " obj_description(p.oid) as description ");
strcat(descbuf, "FROM pg_proc p, pg_type t0, ");
strcat(descbuf, " pg_type t1, pg_type t2, ");
strcat(descbuf, " pg_operator o ");
strcat(descbuf, "WHERE p.prorettype = t0.oid AND ");
strcat(descbuf, " RegprocToOid(o.oprcode) = p.oid AND ");
strcat(descbuf, " p.pronargs = 2 AND ");
strcat(descbuf, " o.oprleft = t1.oid AND ");
strcat(descbuf, " o.oprright = t2.oid ");
if (optarg2)
{
strcat(descbuf, "AND o.oprname ~ '^");
strcat(descbuf, optarg2);
strcat(descbuf, "' ");
}
strcat(descbuf, "UNION ");
strcat(descbuf, "SELECT o.oprname as op, ");
strcat(descbuf, " ''::name AS left_arg, ");
strcat(descbuf, " t1.typname AS right_arg, ");
strcat(descbuf, " t0.typname AS result, ");
if (screen_size.ws_col <= 80)
strcat(descbuf, " substr(obj_description(p.oid),1,41) as description ");
else
strcat(descbuf, " obj_description(p.oid) as description ");
strcat(descbuf, "FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1 ");
strcat(descbuf, "WHERE RegprocToOid(o.oprcode) = p.oid AND ");
strcat(descbuf, " o.oprresult = t0.oid AND ");
strcat(descbuf, " o.oprkind = 'l' AND ");
strcat(descbuf, " o.oprright = t1.oid ");
if (optarg2)
{
strcat(descbuf, "AND o.oprname ~ '^");
strcat(descbuf, optarg2);
strcat(descbuf, "' ");
}
strcat(descbuf, "UNION ");
strcat(descbuf, "SELECT o.oprname as op, ");
strcat(descbuf, " t1.typname AS left_arg, ");
strcat(descbuf, " ''::name AS right_arg, ");
strcat(descbuf, " t0.typname AS result, ");
if (screen_size.ws_col <= 80)
strcat(descbuf, " substr(obj_description(p.oid),1,41) as description ");
else
strcat(descbuf, " obj_description(p.oid) as description ");
strcat(descbuf, "FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1 ");
strcat(descbuf, "WHERE RegprocToOid(o.oprcode) = p.oid AND ");
strcat(descbuf, " o.oprresult = t0.oid AND ");
strcat(descbuf, " o.oprkind = 'r' AND ");
strcat(descbuf, " o.oprleft = t1.oid ");
if (optarg2)
{
strcat(descbuf, "AND o.oprname ~ '^");
strcat(descbuf, optarg2);
strcat(descbuf, "' ");
}
strcat(descbuf, "ORDER BY op, left_arg, right_arg, result;");
success = SendQuery(pset, descbuf, NULL, NULL);
}
else if (strncmp(cmd, "ds", 2) == 0)
/* only sequences */
tableList(pset, false, 'S', false);
else if (strncmp(cmd, "dS", 2) == 0)
/* system tables */
tableList(pset, false, 'b', true);
else if (strncmp(cmd, "dt", 2) == 0)
/* only tables */
tableList(pset, false, 't', false);
else if (strncmp(cmd, "dT", 2) == 0)
{
char descbuf[4096];
/* types */
descbuf[0] = '\0';
strcat(descbuf, "SELECT typname AS type, ");
strcat(descbuf, " obj_description(oid) as description ");
strcat(descbuf, "FROM pg_type ");
strcat(descbuf, "WHERE typrelid = 0 AND ");
strcat(descbuf, " typname !~ '^_.*' ");
strcat(descbuf, "ORDER BY type;");
if (optarg2)
{
strcat(descbuf, "AND typname ~ '^");
strcat(descbuf, optarg2);
strcat(descbuf, "' ");
}
success = SendQuery(pset, descbuf, NULL, NULL);
}
else if (!optarg)
/* show tables, sequences and indices */
tableList(pset, false, 'b', false);
else if (strcmp(optarg, "*") == 0)
{ /* show everything */
if (tableList(pset, false, 'b', false) == 0)
tableList(pset, true, 'b', false);
}
else if (strncmp(cmd, "d ", 2) == 0)
/* describe the specified table */
tableDesc(pset, optarg, NULL);
else
slashUsage(pset);
break;
case 'e': /* edit */
if (query_buf)
do_edit(optarg, query_buf, &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(CMD_TERMINATE);
}
strcpy(lastfile, optarg);
}
else if (!lastfile)
{
fprintf(stderr, "\\r must be followed by a file name initially\n");
break;
}
stat(lastfile, &st);
editFile(lastfile);
#ifndef __CYGWIN32__
if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL))
#else
if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "rb")) == NULL))
#endif
{
perror(lastfile);
break;
}
if (st2.st_mtime == st.st_mtime)
{
if (!pset->quiet)
fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
fclose(fd);
break;
}
MainLoop(pset, fd);
fclose(fd);
break;
}
case 'f':
{
char *fs = DEFAULT_FIELD_SEP;
if (optarg)
fs = optarg;
/* handle \f \{space} */
if (optarg && !*optarg && strlen(cmd) > 1)
{
int i;
/* line and cmd match until the first blank space */
for (i = 2; isspace(line[i]); i++)
;
fs = cmd + i - 1;
}
if (pset->opt.fieldSep)
free(pset->opt.fieldSep);
if (!(pset->opt.fieldSep = strdup(fs)))
{
perror("malloc");
exit(CMD_TERMINATE);
}
if (!pset->quiet)
printf("field separator changed to '%s'\n", pset->opt.fieldSep);
break;
}
case 'g': /* \g means send query */
if (!optarg)
pset->gfname = NULL;
else if (!(pset->gfname = strdup(optarg)))
{
perror("malloc");
exit(CMD_TERMINATE);
}
status = CMD_SEND;
break;
case 'h': /* help */
{
do_help(pset, optarg);
break;
}
case 'i': /* \i is include file */
{
FILE *fd;
if (!optarg)
{
fprintf(stderr, "\\i must be followed by a file name\n");
break;
}
#ifndef __CYGWIN32__
if ((fd = fopen(optarg, "r")) == NULL)
#else
if ((fd = fopen(optarg, "rb")) == NULL)
#endif
{
fprintf(stderr, "file named %s could not be opened\n", optarg);
break;
}
MainLoop(pset, fd);
fclose(fd);
break;
}
case 'H':
if (toggle(pset, &pset->opt.html3, "HTML3.0 tabular output"))
pset->opt.standard = 0;
break;
case 'l': /* \l is list database */
listAllDbs(pset);
break;
case 'm': /* monitor like type-setting */
if (toggle(pset, &pset->opt.standard, "standard SQL separaters and padding"))
{
pset->opt.html3 = pset->opt.expanded = 0;
pset->opt.align = pset->opt.header = 1;
if (pset->opt.fieldSep)
free(pset->opt.fieldSep);
pset->opt.fieldSep = strdup("|");
if (!pset->quiet)
printf("field separator changed to '%s'\n", pset->opt.fieldSep);
}
else
{
if (pset->opt.fieldSep)
free(pset->opt.fieldSep);
pset->opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
if (!pset->quiet)
printf("field separator changed to '%s'\n", pset->opt.fieldSep);
}
break;
case 'o':
setFout(pset, optarg);
break;
case 'p':
if (query_buf && query_buf->len > 0)
{
fputs(query_buf->data, stdout);
fputc('\n', stdout);
}
break;
case 'q': /* \q is quit */
status = CMD_TERMINATE;
break;
case 'r': /* reset(clear) the buffer */
if (query_buf)
{
resetPQExpBuffer(query_buf);
if (!pset->quiet)
printf("buffer reset(cleared)\n");
}
break;
case 's': /* \s is save history to a file */
if (!optarg)
optarg = "/dev/tty";
#ifdef USE_HISTORY
if (write_history(optarg) != 0)
fprintf(stderr, "cannot write history to %s\n", optarg);
#endif
break;
case 't': /* toggle headers */
toggle(pset, &pset->opt.header, "output headings and row count");
break;
case 'T': /* define html <table ...> option */
if (pset->opt.tableOpt)
free(pset->opt.tableOpt);
if (!optarg)
pset->opt.tableOpt = NULL;
else if (!(pset->opt.tableOpt = strdup(optarg)))
{
perror("malloc");
exit(CMD_TERMINATE);
}
break;
case 'w':
{
FILE *fd;
if (!optarg)
{
fprintf(stderr, "\\w must be followed by a file name\n");
break;
}
#ifndef __CYGWIN32__
if ((fd = fopen(optarg, "w")) == NULL)
#else
if ((fd = fopen(optarg, "w")) == NULL)
#endif
{
fprintf(stderr, "file named %s could not be opened\n", optarg);
break;
}
if (query_buf)
fputs(query_buf->data, fd);
fputc('\n', fd);
fclose(fd);
break;
}
case 'x':
toggle(pset, &pset->opt.expanded, "expanded table representation");
break;
case 'z': /* list table rights (grant/revoke) */
rightsList(pset);
break;
case '!':
do_shell(optarg);
break;
default:
case '?': /* \? is help */
slashUsage(pset);
break;
}
free(cmd);
return status;
}
/* MainLoop()
* Main processing loop for reading lines of input
* and sending them to the backend.
*
* This loop is re-entrant. May be called by \i command
* which reads input from a file.
* db_ptr must be initialized and set.
*/
static int
MainLoop(PsqlSettings *pset, FILE *source)
{
PQExpBuffer query_buf; /* buffer for query being accumulated */
char *line; /* current line of input */
char *xcomment; /* start of extended comment */
int len; /* length of the line */
int successResult = 1;
int slashCmdStatus = CMD_SEND;
/*--------------------------------------------------------------
* slashCmdStatus can be:
* CMD_UNKNOWN - send currently constructed query to backend
* (i.e. we got a \g)
* CMD_SEND - send currently constructed query to backend
* (i.e. we got a \g)
* CMD_SKIP_LINE - skip processing of this line, continue building
* up query
* CMD_TERMINATE - terminate processing of this query entirely
* CMD_NEWEDIT - new query supplied by edit
*---------------------------------------------------------------
*/
bool querySent = false;
READ_ROUTINE GetNextLine;
bool eof = false; /* end of our command input? */
bool success;
char in_quote; /* == 0 for no in_quote */
bool was_bslash; /* backslash */
int paren_level;
char *query_start;
/* Stack the prior command source */
FILE *prev_cmd_source = cur_cmd_source;
bool prev_cmd_interactive = cur_cmd_interactive;
/* Establish new source */
cur_cmd_source = source;
cur_cmd_interactive = ((source == stdin) && !pset->notty);
if (cur_cmd_interactive)
{
if (pset->prompt)
free(pset->prompt);
pset->prompt = malloc(strlen(PQdb(pset->db)) + strlen(PROMPT) + 1);
if (pset->quiet)
pset->prompt[0] = '\0';
else
sprintf(pset->prompt, "%s%s", PQdb(pset->db), PROMPT);
if (pset->useReadline)
{
#ifdef USE_HISTORY
using_history();
#endif
GetNextLine = gets_readline;
}
else
GetNextLine = gets_noreadline;
}
else
GetNextLine = gets_fromFile;
query_buf = createPQExpBuffer();
xcomment = NULL;
in_quote = false;
paren_level = 0;
slashCmdStatus = CMD_UNKNOWN; /* set default */
/* main loop to get queries and execute them */
while (!eof)
{
if (slashCmdStatus == CMD_NEWEDIT)
{
/*
* just returned from editing the line? then just copy to the
* input buffer
*/
line = strdup(query_buf->data);
resetPQExpBuffer(query_buf);
/* reset parsing state since we are rescanning whole query */
xcomment = NULL;
in_quote = false;
paren_level = 0;
}
else
{
/*
* otherwise, set interactive prompt if necessary
* and get another line
*/
if (cur_cmd_interactive && !pset->quiet)
{
if (in_quote && in_quote == PROMPT_SINGLEQUOTE)
pset->prompt[strlen(pset->prompt) - 3] = PROMPT_SINGLEQUOTE;
else if (in_quote && in_quote == PROMPT_DOUBLEQUOTE)
pset->prompt[strlen(pset->prompt) - 3] = PROMPT_DOUBLEQUOTE;
else if (xcomment != NULL)
pset->prompt[strlen(pset->prompt) - 3] = PROMPT_COMMENT;
else if (query_buf->len > 0 && !querySent)
pset->prompt[strlen(pset->prompt) - 3] = PROMPT_CONTINUE;
else
pset->prompt[strlen(pset->prompt) - 3] = PROMPT_READY;
}
line = GetNextLine(pset->prompt, source);
#ifdef USE_HISTORY
if (cur_cmd_interactive && pset->useReadline && line != NULL)
add_history(line); /* save non-empty lines in history */
#endif
}
/*
* query_buf holds query already accumulated. line is the malloc'd
* new line of input (note it must be freed before looping around!)
* query_start is the next command start location within the line.
*/
if (line == NULL || (!cur_cmd_interactive && *line == '\0'))
{ /* No more input. Time to quit, or \i
* done */
if (!pset->quiet)
printf("EOF\n");/* Goes on prompt line */
eof = true;
continue;
}
/* not currently inside an extended comment? */
if (xcomment == NULL)
{
query_start = line;
}
else
{
/* otherwise, continue the extended comment... */
query_start = line;
xcomment = line;
}
/* remove whitespaces on the right, incl. \n's */
line = rightTrim(line);
/* echo back if input is from file */
if (!cur_cmd_interactive && !pset->singleStep && !pset->quiet)
fprintf(stderr, "%s\n", line);
slashCmdStatus = CMD_UNKNOWN;
/* nothing on line after trimming? then ignore */
if (line[0] == '\0')
{
free(line);
continue;
}
len = strlen(line);
if (pset->singleLineMode)
{
success = SendQuery(pset, line, NULL, NULL);
successResult &= success;
querySent = true;
}
else
{
int i;
/*
* Parse line, looking for command separators.
*
* The current character is at line[i], the prior character at
* line[i - prevlen], the next character at line[i + thislen].
*/
#ifdef MULTIBYTE
int prevlen = 0;
int thislen = (len > 0) ? PQmblen(line) : 0;
#define ADVANCE_I (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
#else
#define prevlen 1
#define thislen 1
#define ADVANCE_I (i++)
#endif
was_bslash = false;
for (i = 0; i < len; ADVANCE_I)
{
if (line[i] == '\\' && !in_quote)
{
/* backslash command. Copy whatever is before \ to
* query_buf.
*/
char hold_char = line[i];
line[i] = '\0';
if (query_start[0] != '\0')
{
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
appendPQExpBufferStr(query_buf, query_start);
}
line[i] = hold_char;
query_start = line + i;
break; /* go handle backslash command */
}
if (querySent &&
isascii((unsigned char) (line[i])) &&
!isspace(line[i]))
{
resetPQExpBuffer(query_buf);
querySent = false;
}
if (was_bslash)
was_bslash = false;
else if (i > 0 && line[i - prevlen] == '\\')
was_bslash = true;
/* inside a quote? */
if (in_quote && (line[i] != in_quote || was_bslash))
/* do nothing */ ;
/* inside an extended comment? */
else if (xcomment != NULL)
{
if (line[i] == '*' && line[i + thislen] == '/')
{
xcomment = NULL;
ADVANCE_I;
}
}
/* start of extended comment? */
else if (line[i] == '/' && line[i + thislen] == '*')
{
xcomment = line + i;
ADVANCE_I;
}
/* single-line comment? truncate line */
else if ((line[i] == '-' && line[i + thislen] == '-') ||
(line[i] == '/' && line[i + thislen] == '/'))
{
/* print comment at top of query */
if (pset->singleStep)
fprintf(stdout, "%s\n", line + i);
line[i] = '\0'; /* remove comment */
break;
}
else if (in_quote && line[i] == in_quote)
in_quote = false;
else if (!in_quote && (line[i] == '\'' || line[i] == '"'))
in_quote = line[i];
/* semi-colon? then send query now */
else if (!paren_level && line[i] == ';')
{
char hold_char = line[i + thislen];
line[i + thislen] = '\0';
if (query_start[0] != '\0')
{
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
appendPQExpBufferStr(query_buf, query_start);
}
success = SendQuery(pset, query_buf->data, NULL, NULL);
successResult &= success;
line[i + thislen] = hold_char;
query_start = line + i + thislen;
/* sometimes, people do ';\g', don't execute twice */
if (*query_start == '\\' &&
*(query_start + 1) == 'g')
query_start += 2;
querySent = true;
}
else if (line[i] == '(')
{
paren_level++;
}
else if (paren_level && line[i] == ')')
paren_level--;
}
}
/* nothing on line after trimming? then ignore */
if (line[0] == '\0')
{
free(line);
continue;
}
if (!in_quote && query_start[0] == '\\')
{
/* loop to handle \p\g and other backslash combinations */
while (query_start[0] != '\0')
{
char hold_char;
#ifndef WIN32
/* I believe \w \dos\system\x would cause a problem */
/* do we have '\p\g' or '\p \g' ? */
if (strlen(query_start) > 2 &&
query_start[2 + strspn(query_start + 2, " \t")] == '\\')
{
hold_char = query_start[2 + strspn(query_start + 2, " \t")];
query_start[2 + strspn(query_start + 2, " \t")] = '\0';
}
else
/* spread over #endif */
#endif
hold_char = '\0';
slashCmdStatus = HandleSlashCmds(pset,
query_start,
query_buf);
if (slashCmdStatus == CMD_SKIP_LINE && !hold_char)
{
if (query_buf->len == 0)
paren_level = 0;
break;
}
if (slashCmdStatus == CMD_TERMINATE)
break;
query_start += strlen(query_start);
if (hold_char)
query_start[0] = hold_char;
}
free(line);
if (slashCmdStatus == CMD_TERMINATE)
break; /* They did \q, leave the loop */
}
else
{
if (query_start[0] != '\0')
{
querySent = false;
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
appendPQExpBufferStr(query_buf, query_start);
}
free(line);
}
/* had a backslash-g? force the query to be sent */
if (slashCmdStatus == CMD_SEND)
{
success = SendQuery(pset, query_buf->data, NULL, NULL);
successResult &= success;
xcomment = NULL;
in_quote = false;
paren_level = 0;
querySent = true;
}
} /* while */
destroyPQExpBuffer(query_buf);
cur_cmd_source = prev_cmd_source;
cur_cmd_interactive = prev_cmd_interactive;
return successResult;
} /* MainLoop() */
int
main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
char *dbname = NULL;
char *host = NULL;
char *port = NULL;
char *qfilename = NULL;
PsqlSettings settings;
char *singleQuery = NULL;
bool listDatabases = 0;
int successResult = 1;
bool singleSlashCmd = 0;
int c;
char *home = NULL; /* Used to store $HOME */
char *version = NULL; /* PostgreSQL version */
/*
* initialize cur_cmd_source in case we do not use MainLoop ... some
* systems fail if we try to use a static initializer for this :-(
*/
cur_cmd_source = stdin;
cur_cmd_interactive = false;
MemSet(&settings, 0, sizeof settings);
settings.opt.align = 1;
settings.opt.header = 1;
settings.queryFout = stdout;
settings.opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
settings.opt.pager = 1;
if (!isatty(0) || !isatty(1))
{
/* Noninteractive defaults */
settings.notty = 1;
}
else
{
/* Interactive defaults */
pqsignal(SIGINT, handle_sigint); /* control-C => cancel */
#ifdef USE_READLINE
settings.useReadline = 1;
{
/* Set the application name, used for parsing .inputrc */
char *progname = strrchr(argv[0], SEP_CHAR);
rl_readline_name = (progname ? progname+1 : argv[0]);
}
#endif
}
#ifdef PSQL_ALWAYS_GET_PASSWORDS
settings.getPassword = 1;
#else
settings.getPassword = 0;
#endif
#ifdef MULTIBYTE
has_client_encoding = getenv("PGCLIENTENCODING");
#endif
while ((c = getopt(argc, argv, "Aa:c:d:eEf:F:lh:Hnso:p:qStT:ux")) != EOF)
{
switch (c)
{
case 'A':
settings.opt.align = 0;
break;
case 'a':
#ifdef NOT_USED /* this no longer does anything */
fe_setauthsvc(optarg, errbuf);
#endif
break;
case 'c':
singleQuery = strdup(optarg);
if (singleQuery[0] == '\\')
singleSlashCmd = 1;
break;
case 'd':
dbname = optarg;
break;
case 'e':
settings.echoQuery = 1;
break;
case 'E':
settings.echoAllQueries = 1;
settings.echoQuery = 1;
break;
case 'f':
qfilename = optarg;
break;
case 'F':
settings.opt.fieldSep = strdup(optarg);
break;
case 'l':
listDatabases = 1;
break;
case 'h':
host = optarg;
break;
case 'H':
settings.opt.html3 = 1;
break;
case 'n':
settings.useReadline = 0;
break;
case 'o':
setFout(&settings, optarg);
break;
case 'p':
port = optarg;
break;
case 'q':
settings.quiet = 1;
break;
case 's':
settings.singleStep = 1;
break;
case 'S':
settings.singleLineMode = 1;
break;
case 't':
settings.opt.header = 0;
break;
case 'T':
settings.opt.tableOpt = strdup(optarg);
break;
case 'u':
settings.getPassword = 1;
break;
case 'x':
settings.opt.expanded = 1;
break;
default:
usage(argv[0]);
break;
}
}
/* if we still have an argument, use it as the database name */
if (argc - optind == 1)
dbname = argv[optind];
if (listDatabases)
dbname = "template1";
if (settings.getPassword)
{
char username[100];
char password[100];
prompt_for_password(username, password);
settings.db = PQsetdbLogin(host, port, NULL, NULL, dbname,
username, password);
}
else
settings.db = PQsetdb(host, port, NULL, NULL, dbname);
dbname = PQdb(settings.db);
if (PQstatus(settings.db) == CONNECTION_BAD)
{
fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
fprintf(stderr, "%s\n", PQerrorMessage(settings.db));
PQfinish(settings.db);
exit(1);
}
cancelConn = settings.db; /* enable SIGINT to send cancel */
if (listDatabases)
exit(listAllDbs(&settings));
if (!settings.quiet && !settings.notty && !singleQuery && !qfilename)
{
printf("Welcome to the POSTGRESQL interactive sql monitor:\n");
printf(" Please read the file COPYRIGHT for copyright terms "
"of POSTGRESQL\n");
if ((version = selectVersion(&settings)) != NULL)
printf("[%s]\n", version);
printf("\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);
}
/*
* 20.06.97 ACRM See if we've got a /etc/psqlrc or .psqlrc file
*/
if (!access("/etc/psqlrc", R_OK))
HandleSlashCmds(&settings, "\\i /etc/psqlrc", NULL);
if ((home = getenv("HOME")) != NULL)
{
char *psqlrc = NULL,
*line = NULL;
if ((psqlrc = (char *) malloc(strlen(home) + 10)) != NULL)
{
sprintf(psqlrc, "%s/.psqlrc", home);
if (!access(psqlrc, R_OK))
{
if ((line = (char *) malloc(strlen(psqlrc) + 5)) != NULL)
{
sprintf(line, "\\i %s", psqlrc);
HandleSlashCmds(&settings, line, NULL);
free(line);
}
}
free(psqlrc);
}
}
/* End of check for psqlrc files */
if (qfilename || singleSlashCmd)
{
/*
* read in a file full of queries instead of reading in queries
* 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);
}
HandleSlashCmds(&settings, line, NULL);
free(line);
}
else
{
if (singleQuery)
successResult = SendQuery(&settings, singleQuery, NULL, NULL);
else
successResult = MainLoop(&settings, stdin);
}
PQfinish(settings.db);
free(settings.opt.fieldSep);
if (settings.prompt)
free(settings.prompt);
return !successResult;
}
static bool
handleCopyOut(PGconn *conn, FILE *copystream)
{
bool copydone;
char copybuf[COPYBUFSIZ];
int ret;
copydone = false; /* Can't be done; haven't started. */
while (!copydone)
{
ret = PQgetline(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(copystream);
return !PQendcopy(conn);
}
static bool
handleCopyIn(PGconn *conn, const bool mustprompt, FILE *copystream)
{
bool copydone = false;
bool firstload;
bool linedone;
char copybuf[COPYBUFSIZ];
char *s;
int buflen;
int c = 0;
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);
}
while (!copydone)
{ /* for each input line ... */
if (mustprompt)
{
fputs(">> ", stdout);
fflush(stdout);
}
firstload = true;
linedone = false;
while (!linedone)
{ /* for each buffer ... */
s = copybuf;
for (buflen = COPYBUFSIZ; buflen > 1; buflen--)
{
c = getc(copystream);
if (c == '\n' || c == EOF)
{
linedone = true;
break;
}
*s++ = c;
}
*s = '\0';
if (c == EOF)
{
PQputline(conn, "\\.");
copydone = true;
break;
}
PQputline(conn, copybuf);
if (firstload)
{
if (!strcmp(copybuf, "\\."))
copydone = true;
firstload = false;
}
}
PQputline(conn, "\n");
}
return !PQendcopy(conn);
}
/*
* try to open fname and return a FILE *, if it fails, use stdout, instead
*/
static FILE *
setFout(PsqlSettings *pset, char *fname)
{
if (pset->queryFout && pset->queryFout != stdout)
{
if (pset->pipe)
pclose(pset->queryFout);
else
fclose(pset->queryFout);
}
if (!fname)
{
pset->queryFout = stdout;
pqsignal(SIGPIPE, SIG_DFL);
}
else
{
if (*fname == '|')
{
pqsignal(SIGPIPE, SIG_IGN);
#ifndef __CYGWIN32__
pset->queryFout = popen(fname + 1, "w");
#else
pset->queryFout = popen(fname + 1, "wb");
#endif
pset->pipe = 1;
}
else
{
pset->queryFout = fopen(fname, "w");
pqsignal(SIGPIPE, SIG_DFL);
pset->pipe = 0;
}
if (!pset->queryFout)
{
perror(fname);
pset->queryFout = stdout;
}
}
return pset->queryFout;
}
static void
prompt_for_password(char *username, char *password)
{
char buf[512];
int length;
#ifdef HAVE_TERMIOS_H
struct termios t_orig,
t;
#endif
printf("Username: ");
fgets(username, 100, stdin);
length = strlen(username);
/* skip rest of the line */
if (length > 0 && username[length - 1] != '\n')
{
do
{
fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
if (length > 0 && username[length - 1] == '\n')
username[length - 1] = '\0';
printf("Password: ");
#ifdef HAVE_TERMIOS_H
tcgetattr(0, &t);
t_orig = t;
t.c_lflag &= ~ECHO;
tcsetattr(0, TCSADRAIN, &t);
#endif
fgets(password, 100, stdin);
#ifdef HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &t_orig);
#endif
length = strlen(password);
/* skip rest of the line */
if (length > 0 && password[length - 1] != '\n')
{
do
{
fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
if (length > 0 && password[length - 1] == '\n')
password[length - 1] = '\0';
printf("\n\n");
}
static char *
selectVersion(PsqlSettings *pset)
{
#define PGVERSIONBUFSZ 128
static char version[PGVERSIONBUFSZ + 1];
PGresult *res;
char *query = "select version();";
if (!(res = PQexec(pset->db, query)))
return (NULL);
if (PQresultStatus(res) == PGRES_COMMAND_OK ||
PQresultStatus(res) == PGRES_TUPLES_OK)
{
strncpy(version, PQgetvalue(res, 0, 0), PGVERSIONBUFSZ);
version[PGVERSIONBUFSZ] = '\0';
PQclear(res);
return (version);
}
else
{
PQclear(res);
return (NULL);
}
}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: psqlHelp.h,v 1.80 1999/10/29 23:52:22 momjian Exp $ * $Id: psqlHelp.h,v 1.81 1999/11/04 23:14:29 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -384,5 +384,6 @@ TIMEZONE|XACTISOLEVEL|CLIENT_ENCODING|SERVER_ENCODING"}, ...@@ -384,5 +384,6 @@ TIMEZONE|XACTISOLEVEL|CLIENT_ENCODING|SERVER_ENCODING"},
\tVACUUM [VERBOSE] [ANALYZE] [table]\n\ \tVACUUM [VERBOSE] [ANALYZE] [table]\n\
\tor\n\ \tor\n\
\tVACUUM [VERBOSE] ANALYZE [table [(column_name1, ...column_nameN)]];"}, \tVACUUM [VERBOSE] ANALYZE [table [(column_name1, ...column_nameN)]];"},
{NULL, NULL, NULL} /* important to keep a NULL terminator here!*/ {NULL, NULL, NULL} /* important to keep a NULL terminator
* here! */
}; };
...@@ -29,15 +29,18 @@ typedef struct _psqlSettings ...@@ -29,15 +29,18 @@ typedef struct _psqlSettings
char *gfname; /* one-shot file output argument for \g */ char *gfname; /* one-shot file output argument for \g */
bool notty; /* stdin or stdout is not a tty (as determined on startup) */ bool notty; /* stdin or stdout is not a tty (as
* determined on startup) */
bool useReadline; /* use libreadline routines */ bool useReadline; /* use libreadline routines */
bool useHistory; bool useHistory;
bool getPassword; /* prompt the user for a username and bool getPassword; /* prompt the user for a username and
password */ * password */
FILE * cur_cmd_source; /* describe the status of the current main loop */ FILE *cur_cmd_source; /* describe the status of the current main
* loop */
bool cur_cmd_interactive; bool cur_cmd_interactive;
bool has_client_encoding; /* was PGCLIENTENCODING set on startup? */ bool has_client_encoding; /* was PGCLIENTENCODING set on
* startup? */
} PsqlSettings; } PsqlSettings;
......
...@@ -14,244 +14,244 @@ struct _helpStruct ...@@ -14,244 +14,244 @@ struct _helpStruct
static struct _helpStruct QL_HELP[] = { static struct _helpStruct QL_HELP[] = {
{ "TRUNCATE", {"TRUNCATE",
"Empty a table", "Empty a table",
"TRUNCATE [ TABLE ] name" }, "TRUNCATE [ TABLE ] name"},
{ "ABORT", {"ABORT",
"Aborts the current transaction", "Aborts the current transaction",
"ABORT [ WORK | TRANSACTION ]" }, "ABORT [ WORK | TRANSACTION ]"},
{ "ALTER TABLE", {"ALTER TABLE",
"Modifies table properties", "Modifies table properties",
"ALTER TABLE table\n [ * ] ADD [ COLUMN ] ER\">coBLE> type\nALTER TABLE table\n [ * ] RENAME [ COLUMN ] ER\">coBLE> TO newcolumn\nALTER TABLE table\n RENAME TO newtable" }, "ALTER TABLE table\n [ * ] ADD [ COLUMN ] ER\">coBLE> type\nALTER TABLE table\n [ * ] RENAME [ COLUMN ] ER\">coBLE> TO newcolumn\nALTER TABLE table\n RENAME TO newtable"},
{ "ALTER USER", {"ALTER USER",
"Modifies user account information", "Modifies user account information",
"ALTER USER username [ WITH PASSWORD password ]\n [ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]\n [ IN GROUP groupname [, ...] ]\n [ VALID UNTIL 'abstime' ]" }, "ALTER USER username [ WITH PASSWORD password ]\n [ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]\n [ IN GROUP groupname [, ...] ]\n [ VALID UNTIL 'abstime' ]"},
{ "BEGIN", {"BEGIN",
"Begins a transaction in chained mode", "Begins a transaction in chained mode",
"BEGIN [ WORK | TRANSACTION ]" }, "BEGIN [ WORK | TRANSACTION ]"},
{ "CLOSE", {"CLOSE",
"Close a cursor", "Close a cursor",
"CLOSE cursor" }, "CLOSE cursor"},
{ "CLUSTER", {"CLUSTER",
"Gives storage clustering advice to the server", "Gives storage clustering advice to the server",
"CLUSTER indexname ON table" }, "CLUSTER indexname ON table"},
{ "COMMIT", {"COMMIT",
"Commits the current transaction", "Commits the current transaction",
"COMMIT [ WORK | TRANSACTION ]" }, "COMMIT [ WORK | TRANSACTION ]"},
{ "COPY", {"COPY",
"Copies data between files and tables", "Copies data between files and tables",
"COPY [ BINARY ] table [ WITH OIDS ]\n FROM { 'filename' | stdin }\n [ [USING] DELIMITERS 'delimiter' ]\nCOPY [ BINARY ] table [ WITH OIDS ]\n TO { 'filename' | stdout }\n [ [USING] DELIMITERS 'delimiter' ]" }, "COPY [ BINARY ] table [ WITH OIDS ]\n FROM { 'filename' | stdin }\n [ [USING] DELIMITERS 'delimiter' ]\nCOPY [ BINARY ] table [ WITH OIDS ]\n TO { 'filename' | stdout }\n [ [USING] DELIMITERS 'delimiter' ]"},
{ "CREATE AGGREGATE", {"CREATE AGGREGATE",
"Defines a new aggregate function", "Defines a new aggregate function",
"CREATE AGGREGATE name [ AS ] ( BASETYPE = data_type\n [ , SFUNC1 = sfunc1, STYPE1 = sfunc1_return_type ]\n [ , SFUNC2 = sfunc2, STYPE2 = sfunc2_return_type ]\n [ , FINALFUNC = ffunc ]\n [ , INITCOND1 = initial_condition1 ]\n [ , INITCOND2 = initial_condition2 ] )" }, "CREATE AGGREGATE name [ AS ] ( BASETYPE = data_type\n [ , SFUNC1 = sfunc1, STYPE1 = sfunc1_return_type ]\n [ , SFUNC2 = sfunc2, STYPE2 = sfunc2_return_type ]\n [ , FINALFUNC = ffunc ]\n [ , INITCOND1 = initial_condition1 ]\n [ , INITCOND2 = initial_condition2 ] )"},
{ "CREATE DATABASE", {"CREATE DATABASE",
"Creates a new database", "Creates a new database",
"CREATE DATABASE name [ WITH LOCATION = 'dbpath' ]" }, "CREATE DATABASE name [ WITH LOCATION = 'dbpath' ]"},
{ "CREATE FUNCTION", {"CREATE FUNCTION",
"Defines a new function", "Defines a new function",
"CREATE FUNCTION name ( [ ftype [, ...] ] )\n RETURNS rtype\n [ WITH ( attribute [, ...] ) ]\n AS definition \n LANGUAGE 'langname'\n\n\nCREATE FUNCTION name ( [ ftype [, ...] ] )\n RETURNS rtype\n [ WITH ( attribute [, ...] ) ]\n AS obj_file , link_symbol \n LANGUAGE 'C'" }, "CREATE FUNCTION name ( [ ftype [, ...] ] )\n RETURNS rtype\n [ WITH ( attribute [, ...] ) ]\n AS definition \n LANGUAGE 'langname'\n\n\nCREATE FUNCTION name ( [ ftype [, ...] ] )\n RETURNS rtype\n [ WITH ( attribute [, ...] ) ]\n AS obj_file , link_symbol \n LANGUAGE 'C'"},
{ "CREATE INDEX", {"CREATE INDEX",
"Constructs a secondary index", "Constructs a secondary index",
"CREATE [ UNIQUE ] INDEX index_name ON table\n [ USING acc_name ] ( column [ ops_name] [, ...] )\nCREATE [ UNIQUE ] INDEX index_name ON table\n [ USING acc_name ] ( func_name( r\">colle> [, ... ]) ops_name )" }, "CREATE [ UNIQUE ] INDEX index_name ON table\n [ USING acc_name ] ( column [ ops_name] [, ...] )\nCREATE [ UNIQUE ] INDEX index_name ON table\n [ USING acc_name ] ( func_name( r\">colle> [, ... ]) ops_name )"},
{ "CREATE LANGUAGE", {"CREATE LANGUAGE",
"Defines a new language for functions", "Defines a new language for functions",
"CREATE [ TRUSTED ] PROCEDURAL LANGUAGE 'langname'\n HANDLER call_handler\n LANCOMPILER 'comment'" }, "CREATE [ TRUSTED ] PROCEDURAL LANGUAGE 'langname'\n HANDLER call_handler\n LANCOMPILER 'comment'"},
{ "CREATE OPERATOR", {"CREATE OPERATOR",
"Defines a new user operator", "Defines a new user operator",
"CREATE OPERATOR name ( PROCEDURE = func_name\n [, LEFTARG = type1 ] [, RIGHTARG = type2 ]\n [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]\n [, RESTRICT = res_proc ] [, JOIN = join_proc ]\n [, HASHES ] [, SORT1 = left_sort_op ] [, SORT2 = right_sort_op ] )" }, "CREATE OPERATOR name ( PROCEDURE = func_name\n [, LEFTARG = type1 ] [, RIGHTARG = type2 ]\n [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]\n [, RESTRICT = res_proc ] [, JOIN = join_proc ]\n [, HASHES ] [, SORT1 = left_sort_op ] [, SORT2 = right_sort_op ] )"},
{ "CREATE RULE", {"CREATE RULE",
"Defines a new rule", "Defines a new rule",
"CREATE RULE name AS ON event\n TO object [ WHERE condition ]\n DO [ INSTEAD ] [ action | NOTHING ]" }, "CREATE RULE name AS ON event\n TO object [ WHERE condition ]\n DO [ INSTEAD ] [ action | NOTHING ]"},
{ "CREATE SEQUENCE", {"CREATE SEQUENCE",
"Creates a new sequence number generator", "Creates a new sequence number generator",
"CREATE SEQUENCE seqname [ INCREMENT increment ]\n [ MINVALUE minvalue ] [ MAXVALUE maxvalue ]\n [ START start ] [ CACHE cache ] [ CYCLE ]" }, "CREATE SEQUENCE seqname [ INCREMENT increment ]\n [ MINVALUE minvalue ] [ MAXVALUE maxvalue ]\n [ START start ] [ CACHE cache ] [ CYCLE ]"},
{ "CREATE TABLE", {"CREATE TABLE",
"Creates a new table", "Creates a new table",
"CREATE [ TEMPORARY | TEMP ] TABLE table (\n column type\n [ NULL | NOT NULL ] [ UNIQUE ] [ DEFAULT value ]\n [column_constraint_clause | PRIMARY KEY } [ ... ] ]\n [, ... ]\n [, PRIMARY KEY ( column [, ...] ) ]\n [, CHECK ( condition ) ]\n [, table_constraint_clause ]\n ) [ INHERITS ( inherited_table [, ...] ) ]" }, "CREATE [ TEMPORARY | TEMP ] TABLE table (\n column type\n [ NULL | NOT NULL ] [ UNIQUE ] [ DEFAULT value ]\n [column_constraint_clause | PRIMARY KEY } [ ... ] ]\n [, ... ]\n [, PRIMARY KEY ( column [, ...] ) ]\n [, CHECK ( condition ) ]\n [, table_constraint_clause ]\n ) [ INHERITS ( inherited_table [, ...] ) ]"},
{ "CREATE TABLE AS", {"CREATE TABLE AS",
"Creates a new table", "Creates a new table",
"CREATE TABLE table [ (column [, ...] ) ]\n AS select_clause" }, "CREATE TABLE table [ (column [, ...] ) ]\n AS select_clause"},
{ "CREATE TRIGGER", {"CREATE TRIGGER",
"Creates a new trigger", "Creates a new trigger",
"CREATE TRIGGER name { BEFORE | AFTER } { event [OR ...] }\n ON table FOR EACH { ROW | STATEMENT }\n EXECUTE PROCEDURE ER\">funcBLE> ( arguments )" }, "CREATE TRIGGER name { BEFORE | AFTER } { event [OR ...] }\n ON table FOR EACH { ROW | STATEMENT }\n EXECUTE PROCEDURE ER\">funcBLE> ( arguments )"},
{ "CREATE TYPE", {"CREATE TYPE",
"Defines a new base data type", "Defines a new base data type",
"CREATE TYPE typename ( INPUT = input_function, OUTPUT = output_function\n , INTERNALLENGTH = { internallength | VARIABLE } [ , EXTERNALLENGTH = { externallength | VARIABLE } ]\n [ , DEFAULT = \"default\" ]\n [ , ELEMENT = element ] [ , DELIMITER = delimiter ]\n [ , SEND = send_function ] [ , RECEIVE = receive_function ]\n [ , PASSEDBYVALUE ] )" }, "CREATE TYPE typename ( INPUT = input_function, OUTPUT = output_function\n , INTERNALLENGTH = { internallength | VARIABLE } [ , EXTERNALLENGTH = { externallength | VARIABLE } ]\n [ , DEFAULT = \"default\" ]\n [ , ELEMENT = element ] [ , DELIMITER = delimiter ]\n [ , SEND = send_function ] [ , RECEIVE = receive_function ]\n [ , PASSEDBYVALUE ] )"},
{ "CREATE USER", {"CREATE USER",
"Creates account information for a new user", "Creates account information for a new user",
"CREATE USER username\n [ WITH PASSWORD password ]\n [ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]\n [ IN GROUP groupname [, ...] ]\n [ VALID UNTIL 'abstime' ]" }, "CREATE USER username\n [ WITH PASSWORD password ]\n [ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]\n [ IN GROUP groupname [, ...] ]\n [ VALID UNTIL 'abstime' ]"},
{ "CREATE VIEW", {"CREATE VIEW",
"Constructs a virtual table", "Constructs a virtual table",
"CREATE VIEW view AS SELECT query" }, "CREATE VIEW view AS SELECT query"},
{ "DECLARE", {"DECLARE",
"Defines a cursor for table access", "Defines a cursor for table access",
"DECLARE cursor [ BINARY ] [ INSENSITIVE ] [ SCROLL ]\n CURSOR FOR query\n [ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]" }, "DECLARE cursor [ BINARY ] [ INSENSITIVE ] [ SCROLL ]\n CURSOR FOR query\n [ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]"},
{ "DELETE", {"DELETE",
"Removes rows from a table", "Removes rows from a table",
"DELETE FROM table [ WHERE condition ]" }, "DELETE FROM table [ WHERE condition ]"},
{ "DROP AGGREGATE", {"DROP AGGREGATE",
"Removes the definition of an aggregate function", "Removes the definition of an aggregate function",
"DROP AGGREGATE name type" }, "DROP AGGREGATE name type"},
{ "FETCH", {"FETCH",
"Gets rows using a cursor", "Gets rows using a cursor",
"FETCH [ selector ] [ count ] { IN | FROM } cursor\nFETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor" }, "FETCH [ selector ] [ count ] { IN | FROM } cursor\nFETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"},
{ "DROP DATABASE", {"DROP DATABASE",
"Destroys an existing database", "Destroys an existing database",
"DROP DATABASE name" }, "DROP DATABASE name"},
{ "DROP FUNCTION", {"DROP FUNCTION",
"Removes a user-defined C function", "Removes a user-defined C function",
"DROP FUNCTION name ( [ type [, ...] ] )" }, "DROP FUNCTION name ( [ type [, ...] ] )"},
{ "DROP INDEX", {"DROP INDEX",
"Removes an index from a database", "Removes an index from a database",
"DROP INDEX index_name" }, "DROP INDEX index_name"},
{ "DROP LANGUAGE", {"DROP LANGUAGE",
"Removes a user-defined procedural language", "Removes a user-defined procedural language",
"DROP PROCEDURAL LANGUAGE 'name'" }, "DROP PROCEDURAL LANGUAGE 'name'"},
{ "DROP OPERATOR", {"DROP OPERATOR",
"Removes an operator from the database", "Removes an operator from the database",
"DROP OPERATOR id ( type | NONE [,...] )" }, "DROP OPERATOR id ( type | NONE [,...] )"},
{ "DROP RULE", {"DROP RULE",
"Removes an existing rule from the database", "Removes an existing rule from the database",
"DROP RULE name" }, "DROP RULE name"},
{ "DROP SEQUENCE", {"DROP SEQUENCE",
"Removes an existing sequence", "Removes an existing sequence",
"DROP SEQUENCE name [, ...]" }, "DROP SEQUENCE name [, ...]"},
{ "DROP TABLE", {"DROP TABLE",
"Removes existing tables from a database", "Removes existing tables from a database",
"DROP TABLE name [, ...]" }, "DROP TABLE name [, ...]"},
{ "DROP TRIGGER", {"DROP TRIGGER",
"Removes the definition of a trigger", "Removes the definition of a trigger",
"DROP TRIGGER name ON table" }, "DROP TRIGGER name ON table"},
{ "DROP TYPE", {"DROP TYPE",
"Removes a user-defined type from the system catalogs", "Removes a user-defined type from the system catalogs",
"DROP TYPE typename" }, "DROP TYPE typename"},
{ "DROP USER", {"DROP USER",
"Removes an user account information", "Removes an user account information",
"DROP USER name" }, "DROP USER name"},
{ "DROP VIEW", {"DROP VIEW",
"Removes an existing view from a database", "Removes an existing view from a database",
"DROP VIEW name" }, "DROP VIEW name"},
{ "EXPLAIN", {"EXPLAIN",
"Shows statement execution details", "Shows statement execution details",
"EXPLAIN [ VERBOSE ] query" }, "EXPLAIN [ VERBOSE ] query"},
{ "GRANT", {"GRANT",
"Grants access privilege to a user, a group or all users", "Grants access privilege to a user, a group or all users",
"GRANT privilege [, ...] ON object [, ...]\n TO { PUBLIC | GROUP group | username }" }, "GRANT privilege [, ...] ON object [, ...]\n TO { PUBLIC | GROUP group | username }"},
{ "INSERT", {"INSERT",
"Inserts new rows into a table", "Inserts new rows into a table",
"INSERT INTO table [ ( column [, ...] ) ]\n { VALUES ( expression [, ...] ) | SELECT query }" }, "INSERT INTO table [ ( column [, ...] ) ]\n { VALUES ( expression [, ...] ) | SELECT query }"},
{ "LISTEN", {"LISTEN",
"Listen for a response on a notify condition", "Listen for a response on a notify condition",
"LISTEN name" }, "LISTEN name"},
{ "LOAD", {"LOAD",
"Dynamically loads an object file", "Dynamically loads an object file",
"LOAD 'filename'" }, "LOAD 'filename'"},
{ "LOCK", {"LOCK",
"Explicitly lock a table inside a transaction", "Explicitly lock a table inside a transaction",
"LOCK [ TABLE ] name\nLOCK [ TABLE ] name IN [ ROW | ACCESS ] { SHARE | EXCLUSIVE } MODE\nLOCK [ TABLE ] name IN SHARE ROW EXCLUSIVE MODE" }, "LOCK [ TABLE ] name\nLOCK [ TABLE ] name IN [ ROW | ACCESS ] { SHARE | EXCLUSIVE } MODE\nLOCK [ TABLE ] name IN SHARE ROW EXCLUSIVE MODE"},
{ "MOVE", {"MOVE",
"Moves cursor position", "Moves cursor position",
"MOVE [ selector ] [ count ] \n { IN | FROM } cursor\n FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor" }, "MOVE [ selector ] [ count ] \n { IN | FROM } cursor\n FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"},
{ "NOTIFY", {"NOTIFY",
"Signals all frontends and backends listening on a notify condition", "Signals all frontends and backends listening on a notify condition",
"NOTIFY name" }, "NOTIFY name"},
{ "RESET", {"RESET",
"Restores run-time parameters for session to default values", "Restores run-time parameters for session to default values",
"RESET variable" }, "RESET variable"},
{ "REVOKE", {"REVOKE",
"Revokes access privilege from a user, a group or all users.", "Revokes access privilege from a user, a group or all users.",
"REVOKE privilege [, ...]\n ON object [, ...]\n FROM { PUBLIC | GROUP ER\">gBLE> | username }" }, "REVOKE privilege [, ...]\n ON object [, ...]\n FROM { PUBLIC | GROUP ER\">gBLE> | username }"},
{ "ROLLBACK", {"ROLLBACK",
"Aborts the current transaction", "Aborts the current transaction",
"ROLLBACK [ WORK | TRANSACTION ]" }, "ROLLBACK [ WORK | TRANSACTION ]"},
{ "SELECT", {"SELECT",
"Retrieve rows from a table or view.", "Retrieve rows from a table or view.",
"SELECT [ ALL | DISTINCT [ ON column ] ]\n expression [ AS name ] [, ...]\n [ INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table ]\n [ FROM table [ alias ] [, ...] ]\n [ WHERE condition ]\n [ GROUP BY column [, ...] ]\n [ HAVING condition [, ...] ]\n [ { UNION [ ALL ] | INTERSECT | EXCEPT } select ]\n [ ORDER BY column [ ASC | DESC ] [, ...] ]\n [ FOR UPDATE [ OF class_name... ] ]\n [ LIMIT { count | ALL } [ { OFFSET | , } count ] ]" }, "SELECT [ ALL | DISTINCT [ ON column ] ]\n expression [ AS name ] [, ...]\n [ INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table ]\n [ FROM table [ alias ] [, ...] ]\n [ WHERE condition ]\n [ GROUP BY column [, ...] ]\n [ HAVING condition [, ...] ]\n [ { UNION [ ALL ] | INTERSECT | EXCEPT } select ]\n [ ORDER BY column [ ASC | DESC ] [, ...] ]\n [ FOR UPDATE [ OF class_name... ] ]\n [ LIMIT { count | ALL } [ { OFFSET | , } count ] ]"},
{ "SELECT INTO", {"SELECT INTO",
"Create a new table from an existing table or view", "Create a new table from an existing table or view",
"SELECT [ ALL | DISTINCT ] expression [ AS name ] [, ...]\n INTO [TEMP] [ TABLE ] new_table ]\n [ FROM table [alias] [, ...] ]\n [ WHERE condition ]\n [ GROUP BY column [, ...] ]\n [ HAVING condition [, ...] ]\n [ { UNION [ALL] | INTERSECT | EXCEPT } select]\n [ ORDER BY column [ ASC | DESC ] [, ...] ]\n [ FOR UPDATE [OF class_name...]]\n [ LIMIT count [OFFSET|, count]]" }, "SELECT [ ALL | DISTINCT ] expression [ AS name ] [, ...]\n INTO [TEMP] [ TABLE ] new_table ]\n [ FROM table [alias] [, ...] ]\n [ WHERE condition ]\n [ GROUP BY column [, ...] ]\n [ HAVING condition [, ...] ]\n [ { UNION [ALL] | INTERSECT | EXCEPT } select]\n [ ORDER BY column [ ASC | DESC ] [, ...] ]\n [ FOR UPDATE [OF class_name...]]\n [ LIMIT count [OFFSET|, count]]"},
{ "SET", {"SET",
"Set run-time parameters for session", "Set run-time parameters for session",
"SET variable { TO | = } { 'value' | DEFAULT }\nSET TIME ZONE { 'timezone' | LOCAL | DEFAULT }\nSET TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE }" }, "SET variable { TO | = } { 'value' | DEFAULT }\nSET TIME ZONE { 'timezone' | LOCAL | DEFAULT }\nSET TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE }"},
{ "SHOW", {"SHOW",
"Shows run-time parameters for session", "Shows run-time parameters for session",
"SHOW keyword" }, "SHOW keyword"},
{ "UNLISTEN", {"UNLISTEN",
"Stop listening for notification", "Stop listening for notification",
"UNLISTEN { notifyname | * }" }, "UNLISTEN { notifyname | * }"},
{ "UPDATE", {"UPDATE",
"Replaces values of columns in a table", "Replaces values of columns in a table",
"UPDATE table SET R\">colle> = expression [, ...]\n [ FROM fromlist ]\n [ WHERE condition ]" }, "UPDATE table SET R\">colle> = expression [, ...]\n [ FROM fromlist ]\n [ WHERE condition ]"},
{ "VACUUM", {"VACUUM",
"Clean and analyze a Postgres database", "Clean and analyze a Postgres database",
"VACUUM [ VERBOSE ] [ ANALYZE ] [ table ]\nVACUUM [ VERBOSE ] ANALYZE [ ER\">tBLE> [ (column [, ...] ) ] ]" }, "VACUUM [ VERBOSE ] [ ANALYZE ] [ table ]\nVACUUM [ VERBOSE ] ANALYZE [ ER\">tBLE> [ (column [, ...] ) ] ]"},
{ "END", {"END",
"Commits the current transaction", "Commits the current transaction",
"END [ WORK | TRANSACTION ]" }, "END [ WORK | TRANSACTION ]"},
{ "COMMENT", {"COMMENT",
"Add comment to an object", "Add comment to an object",
"COMMENT ON\n[\n [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]\n object_name |\n COLUMN table_name.column_name|\n AGGREGATE agg_name agg_type|\n FUNCTION func_name (arg1, arg2, ...)|\n OPERATOR op (leftoperand_type rightoperand_type) |\n TRIGGER trigger_name ON table_name\n] IS 'text'" }, "COMMENT ON\n[\n [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]\n object_name |\n COLUMN table_name.column_name|\n AGGREGATE agg_name agg_type|\n FUNCTION func_name (arg1, arg2, ...)|\n OPERATOR op (leftoperand_type rightoperand_type) |\n TRIGGER trigger_name ON table_name\n] IS 'text'"},
{ NULL, NULL, NULL } /* End of list marker */ {NULL, NULL, NULL} /* End of list marker */
}; };
#endif /* SQL_HELP_H */ #endif /* SQL_HELP_H */
...@@ -37,16 +37,18 @@ ...@@ -37,16 +37,18 @@
static void static void
process_psqlrc(PsqlSettings * pset); process_psqlrc(PsqlSettings *pset);
static void static void
showVersion(PsqlSettings *pset, bool verbose); showVersion(PsqlSettings *pset, bool verbose);
/* Structures to pass information between the option parsing routine /* Structures to pass information between the option parsing routine
* and the main function * and the main function
*/ */
enum _actions { ACT_NOTHING = 0, enum _actions
{
ACT_NOTHING = 0,
ACT_SINGLE_SLASH, ACT_SINGLE_SLASH,
ACT_LIST_DB, ACT_LIST_DB,
ACT_SHOW_VER, ACT_SHOW_VER,
...@@ -54,18 +56,19 @@ enum _actions { ACT_NOTHING = 0, ...@@ -54,18 +56,19 @@ enum _actions { ACT_NOTHING = 0,
ACT_FILE ACT_FILE
}; };
struct adhoc_opts { struct adhoc_opts
char * dbname; {
char * host; char *dbname;
char * port; char *host;
char * username; char *port;
char *username;
enum _actions action; enum _actions action;
char * action_string; char *action_string;
bool no_readline; bool no_readline;
}; };
static void static void
parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * options); parse_options(int argc, char *argv[], PsqlSettings *pset, struct adhoc_opts * options);
...@@ -81,8 +84,8 @@ main(int argc, char **argv) ...@@ -81,8 +84,8 @@ main(int argc, char **argv)
struct adhoc_opts options; struct adhoc_opts options;
int successResult; int successResult;
char * username = NULL; char *username = NULL;
char * password = NULL; char *password = NULL;
bool need_pass; bool need_pass;
MemSet(&settings, 0, sizeof settings); MemSet(&settings, 0, sizeof settings);
...@@ -115,11 +118,12 @@ main(int argc, char **argv) ...@@ -115,11 +118,12 @@ main(int argc, char **argv)
parse_options(argc, argv, &settings, &options); parse_options(argc, argv, &settings, &options);
if (options.action==ACT_LIST_DB || options.action==ACT_SHOW_VER) if (options.action == ACT_LIST_DB || options.action == ACT_SHOW_VER)
options.dbname = "template1"; options.dbname = "template1";
if (options.username) { if (options.username)
if (strcmp(options.username, "?")==0) {
if (strcmp(options.username, "?") == 0)
username = simple_prompt("Username: ", 100, true); username = simple_prompt("Username: ", 100, true);
else else
username = strdup(options.username); username = strdup(options.username);
...@@ -129,12 +133,14 @@ main(int argc, char **argv) ...@@ -129,12 +133,14 @@ main(int argc, char **argv)
password = simple_prompt("Password: ", 100, false); password = simple_prompt("Password: ", 100, false);
/* loop until we have a password if requested by backend */ /* loop until we have a password if requested by backend */
do { do
{
need_pass = false; need_pass = false;
settings.db = PQsetdbLogin(options.host, options.port, NULL, NULL, options.dbname, username, password); settings.db = PQsetdbLogin(options.host, options.port, NULL, NULL, options.dbname, username, password);
if (PQstatus(settings.db)==CONNECTION_BAD && if (PQstatus(settings.db) == CONNECTION_BAD &&
strcmp(PQerrorMessage(settings.db), "fe_sendauth: no password supplied\n")==0) { strcmp(PQerrorMessage(settings.db), "fe_sendauth: no password supplied\n") == 0)
{
need_pass = true; need_pass = true;
free(password); free(password);
password = NULL; password = NULL;
...@@ -145,22 +151,26 @@ main(int argc, char **argv) ...@@ -145,22 +151,26 @@ main(int argc, char **argv)
free(username); free(username);
free(password); free(password);
if (PQstatus(settings.db) == CONNECTION_BAD) { if (PQstatus(settings.db) == CONNECTION_BAD)
{
fprintf(stderr, "Connection to database '%s' failed.\n%s\n", PQdb(settings.db), PQerrorMessage(settings.db)); fprintf(stderr, "Connection to database '%s' failed.\n%s\n", PQdb(settings.db), PQerrorMessage(settings.db));
PQfinish(settings.db); PQfinish(settings.db);
exit(EXIT_BADCONN); exit(EXIT_BADCONN);
} }
if (options.action == ACT_LIST_DB) { if (options.action == ACT_LIST_DB)
{
int success = listAllDbs(&settings); int success = listAllDbs(&settings);
PQfinish(settings.db); PQfinish(settings.db);
exit (!success); exit(!success);
} }
if (options.action == ACT_SHOW_VER) { if (options.action == ACT_SHOW_VER)
{
showVersion(&settings, true); showVersion(&settings, true);
PQfinish(settings.db); PQfinish(settings.db);
exit (EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
...@@ -169,7 +179,7 @@ main(int argc, char **argv) ...@@ -169,7 +179,7 @@ main(int argc, char **argv)
puts("Welcome to psql, the PostgreSQL interactive terminal.\n" puts("Welcome to psql, the PostgreSQL interactive terminal.\n"
"(Please type \\copyright to see the distribution terms of PostgreSQL.)"); "(Please type \\copyright to see the distribution terms of PostgreSQL.)");
// showVersion(&settings, false); //showVersion(&settings, false);
puts("\n" puts("\n"
"Type \\h for help with SQL commands,\n" "Type \\h for help with SQL commands,\n"
...@@ -215,46 +225,48 @@ main(int argc, char **argv) ...@@ -215,46 +225,48 @@ main(int argc, char **argv)
#ifdef WIN32 #ifdef WIN32
/* getopt is not in the standard includes on Win32 */ /* getopt is not in the standard includes on Win32 */
int getopt(int, char *const[], const char *); int getopt(int, char *const[], const char *);
#endif #endif
static void static void
parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * options) parse_options(int argc, char *argv[], PsqlSettings *pset, struct adhoc_opts * options)
{ {
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static struct option long_options[] = { static struct option long_options[] = {
{ "no-align", no_argument, NULL, 'A' }, {"no-align", no_argument, NULL, 'A'},
{ "command", required_argument, NULL, 'c' }, {"command", required_argument, NULL, 'c'},
{ "database", required_argument, NULL, 'd' }, {"database", required_argument, NULL, 'd'},
{ "dbname", required_argument, NULL, 'd' }, {"dbname", required_argument, NULL, 'd'},
{ "echo", no_argument, NULL, 'e' }, {"echo", no_argument, NULL, 'e'},
{ "echo-queries", no_argument, NULL, 'e' }, {"echo-queries", no_argument, NULL, 'e'},
{ "echo-all", no_argument, NULL, 'E' }, {"echo-all", no_argument, NULL, 'E'},
{ "echo-all-queries", no_argument, NULL, 'E' }, {"echo-all-queries", no_argument, NULL, 'E'},
{ "file", required_argument, NULL, 'f' }, {"file", required_argument, NULL, 'f'},
{ "field-sep", required_argument, NULL, 'F' }, {"field-sep", required_argument, NULL, 'F'},
{ "host", required_argument, NULL, 'h' }, {"host", required_argument, NULL, 'h'},
{ "html", no_argument, NULL, 'H' }, {"html", no_argument, NULL, 'H'},
{ "list", no_argument, NULL, 'l' }, {"list", no_argument, NULL, 'l'},
{ "no-readline", no_argument, NULL, 'n' }, {"no-readline", no_argument, NULL, 'n'},
{ "out", required_argument, NULL, 'o' }, {"out", required_argument, NULL, 'o'},
{ "to-file", required_argument, NULL, 'o' }, {"to-file", required_argument, NULL, 'o'},
{ "port", required_argument, NULL, 'p' }, {"port", required_argument, NULL, 'p'},
{ "pset", required_argument, NULL, 'P' }, {"pset", required_argument, NULL, 'P'},
{ "quiet", no_argument, NULL, 'q' }, {"quiet", no_argument, NULL, 'q'},
{ "single-step", no_argument, NULL, 's' }, {"single-step", no_argument, NULL, 's'},
{ "single-line", no_argument, NULL, 'S' }, {"single-line", no_argument, NULL, 'S'},
{ "tuples-only", no_argument, NULL, 't' }, {"tuples-only", no_argument, NULL, 't'},
{ "table-attr", required_argument, NULL, 'T' }, {"table-attr", required_argument, NULL, 'T'},
{ "username", required_argument, NULL, 'U' }, {"username", required_argument, NULL, 'U'},
{ "expanded", no_argument, NULL, 'x' }, {"expanded", no_argument, NULL, 'x'},
{ "set", required_argument, NULL, 'v' }, {"set", required_argument, NULL, 'v'},
{ "variable", required_argument, NULL, 'v' }, {"variable", required_argument, NULL, 'v'},
{ "version", no_argument, NULL, 'V' }, {"version", no_argument, NULL, 'V'},
{ "password", no_argument, NULL, 'W' }, {"password", no_argument, NULL, 'W'},
{ "help", no_argument, NULL, '?' }, {"help", no_argument, NULL, '?'},
}; };
int optindex; int optindex;
#endif #endif
extern char *optarg; extern char *optarg;
...@@ -266,7 +278,11 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o ...@@ -266,7 +278,11 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
while ((c = getopt_long(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?", long_options, &optindex)) != -1) while ((c = getopt_long(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?", long_options, &optindex)) != -1)
#else #else
/* Be sure to leave the '-' in here, so we can catch accidental long options. */
/*
* Be sure to leave the '-' in here, so we can catch accidental long
* options.
*/
while ((c = getopt(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?-")) != -1) while ((c = getopt(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?-")) != -1)
#endif #endif
{ {
...@@ -326,12 +342,14 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o ...@@ -326,12 +342,14 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
equal_loc = strchr(value, '='); equal_loc = strchr(value, '=');
if (!equal_loc) if (!equal_loc)
result = do_pset(value, NULL, &pset->popt, true); result = do_pset(value, NULL, &pset->popt, true);
else { else
{
*equal_loc = '\0'; *equal_loc = '\0';
result = do_pset(value, equal_loc+1, &pset->popt, true); result = do_pset(value, equal_loc + 1, &pset->popt, true);
} }
if (!result) { if (!result)
{
fprintf(stderr, "Couldn't set printing paramter %s.\n", value); fprintf(stderr, "Couldn't set printing paramter %s.\n", value);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -371,15 +389,19 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o ...@@ -371,15 +389,19 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
value = xstrdup(optarg); value = xstrdup(optarg);
equal_loc = strchr(value, '='); equal_loc = strchr(value, '=');
if (!equal_loc) { if (!equal_loc)
if (!DeleteVariable(pset->vars, value)) { {
if (!DeleteVariable(pset->vars, value))
{
fprintf(stderr, "Couldn't delete variable %s.\n", value); fprintf(stderr, "Couldn't delete variable %s.\n", value);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
else { else
{
*equal_loc = '\0'; *equal_loc = '\0';
if (!SetVariable(pset->vars, value, equal_loc+1)) { if (!SetVariable(pset->vars, value, equal_loc + 1))
{
fprintf(stderr, "Couldn't set variable %s to %s.\n", value, equal_loc); fprintf(stderr, "Couldn't set variable %s to %s.\n", value, equal_loc);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -412,8 +434,12 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o ...@@ -412,8 +434,12 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
} }
} }
/* if we still have arguments, use it as the database name and username */ /*
while (argc - optind >= 1) { * if we still have arguments, use it as the database name and
* username
*/
while (argc - optind >= 1)
{
if (!options->dbname) if (!options->dbname)
options->dbname = argv[optind]; options->dbname = argv[optind];
else if (!options->username) else if (!options->username)
...@@ -431,35 +457,38 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o ...@@ -431,35 +457,38 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
* Load /etc/psqlrc or .psqlrc file, if found. * Load /etc/psqlrc or .psqlrc file, if found.
*/ */
static void static void
process_psqlrc(PsqlSettings * pset) process_psqlrc(PsqlSettings *pset)
{ {
char *psqlrc; char *psqlrc;
char * home; char *home;
#ifdef WIN32 #ifdef WIN32
#define R_OK 0 #define R_OK 0
#endif #endif
/* System-wide startup file */ /* System-wide startup file */
if (access("/etc/psqlrc-"PG_RELEASE"."PG_VERSION"."PG_SUBVERSION, R_OK) == 0) if (access("/etc/psqlrc-" PG_RELEASE "." PG_VERSION "." PG_SUBVERSION, R_OK) == 0)
process_file("/etc/psqlrc-"PG_RELEASE"."PG_VERSION"."PG_SUBVERSION, pset); process_file("/etc/psqlrc-" PG_RELEASE "." PG_VERSION "." PG_SUBVERSION, pset);
else if (access("/etc/psqlrc", R_OK) == 0) else if (access("/etc/psqlrc", R_OK) == 0)
process_file("/etc/psqlrc", pset); process_file("/etc/psqlrc", pset);
/* Look for one in the home dir */ /* Look for one in the home dir */
home = getenv("HOME"); home = getenv("HOME");
if (home) { if (home)
{
psqlrc = (char *) malloc(strlen(home) + 20); psqlrc = (char *) malloc(strlen(home) + 20);
if (!psqlrc) { if (!psqlrc)
{
perror("malloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
sprintf(psqlrc, "%s/.psqlrc-"PG_RELEASE"."PG_VERSION"."PG_SUBVERSION, home); sprintf(psqlrc, "%s/.psqlrc-" PG_RELEASE "." PG_VERSION "." PG_SUBVERSION, home);
if (access(psqlrc, R_OK) == 0) if (access(psqlrc, R_OK) == 0)
process_file(psqlrc, pset); process_file(psqlrc, pset);
else { else
{
sprintf(psqlrc, "%s/.psqlrc", home); sprintf(psqlrc, "%s/.psqlrc", home);
if (access(psqlrc, R_OK) == 0) if (access(psqlrc, R_OK) == 0)
process_file(psqlrc, pset); process_file(psqlrc, pset);
...@@ -484,31 +513,37 @@ showVersion(PsqlSettings *pset, bool verbose) ...@@ -484,31 +513,37 @@ showVersion(PsqlSettings *pset, bool verbose)
{ {
PGresult *res; PGresult *res;
char *versionstr = NULL; char *versionstr = NULL;
long int release = 0, version = 0, subversion = 0; long int release = 0,
version = 0,
subversion = 0;
/* get backend version */ /* get backend version */
res = PSQLexec(pset, "SELECT version()"); res = PSQLexec(pset, "SELECT version()");
if (PQresultStatus(res) == PGRES_TUPLES_OK) if (PQresultStatus(res) == PGRES_TUPLES_OK)
versionstr = PQgetvalue(res, 0, 0); versionstr = PQgetvalue(res, 0, 0);
if (!verbose) { if (!verbose)
if (versionstr) puts(versionstr); {
if (versionstr)
puts(versionstr);
PQclear(res); PQclear(res);
return; return;
} }
if (strncmp(versionstr, "PostgreSQL ", 11) == 0) { if (strncmp(versionstr, "PostgreSQL ", 11) == 0)
{
char *tmp; char *tmp;
release = strtol(&versionstr[11], &tmp, 10); release = strtol(&versionstr[11], &tmp, 10);
version = strtol(tmp+1, &tmp, 10); version = strtol(tmp + 1, &tmp, 10);
subversion = strtol(tmp+1, &tmp, 10); subversion = strtol(tmp + 1, &tmp, 10);
} }
printf("Server: %s\npsql", versionstr ? versionstr : "(could not connected)"); printf("Server: %s\npsql", versionstr ? versionstr : "(could not connected)");
if (strcmp(versionstr, PG_VERSION_STR) != 0) if (strcmp(versionstr, PG_VERSION_STR) != 0)
printf(&PG_VERSION_STR[strcspn(PG_VERSION_STR, " ")]); printf(&PG_VERSION_STR[strcspn(PG_VERSION_STR, " ")]);
printf(" ("__DATE__" "__TIME__")"); printf(" (" __DATE__ " " __TIME__ ")");
#ifdef MULTIBYTE #ifdef MULTIBYTE
printf(", multibyte"); printf(", multibyte");
...@@ -535,7 +570,7 @@ showVersion(PsqlSettings *pset, bool verbose) ...@@ -535,7 +570,7 @@ showVersion(PsqlSettings *pset, bool verbose)
puts(""); puts("");
if (release < 6 || (release == 6 && version < 5)) if (release < 6 || (release == 6 && version < 5))
puts ("\nWarning: The server you are connected to is potentially too old for this client\n" puts("\nWarning: The server you are connected to is potentially too old for this client\n"
"version. You should ideally be using clients and servers from the same\n" "version. You should ideally be using clients and servers from the same\n"
"distribution."); "distribution.");
......
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
#include <c.h> #include <c.h>
#include "stringutils.h" #include "stringutils.h"
//#include <ctype.h> //
#include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
//#include <stdio.h> //
#include <stdio.h>
#include <postgres.h> #include <postgres.h>
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
...@@ -17,7 +19,7 @@ ...@@ -17,7 +19,7 @@
static void static void
unescape_quotes(char *source, char quote, char escape); unescape_quotes(char *source, char quote, char escape);
/* /*
...@@ -34,21 +36,26 @@ unescape_quotes(char *source, char quote, char escape); ...@@ -34,21 +36,26 @@ unescape_quotes(char *source, char quote, char escape);
* *
* Note that the string s is _not_ overwritten in this implementation. * Note that the string s is _not_ overwritten in this implementation.
*/ */
char * strtokx(const char *s, char *
strtokx(const char *s,
const char *delim, const char *delim,
const char *quote, const char *quote,
char escape, char escape,
char * was_quoted, char *was_quoted,
unsigned int * token_pos) unsigned int *token_pos)
{ {
static char * storage = NULL; /* store the local copy of the users string here */ static char *storage = NULL;/* store the local copy of the users
static char * string = NULL; /* pointer into storage where to continue on next call */ * string here */
static char *string = NULL; /* pointer into storage where to continue
* on next call */
/* variously abused variables: */ /* variously abused variables: */
unsigned int offset; unsigned int offset;
char * start; char *start;
char *cp = NULL; char *cp = NULL;
if (s) { if (s)
{
free(storage); free(storage);
storage = strdup(s); storage = strdup(s);
string = storage; string = storage;
...@@ -61,7 +68,8 @@ char * strtokx(const char *s, ...@@ -61,7 +68,8 @@ char * strtokx(const char *s,
offset = strspn(string, delim); offset = strspn(string, delim);
/* end of string reached */ /* end of string reached */
if (string[offset] == '\0') { if (string[offset] == '\0')
{
/* technically we don't need to free here, but we're nice */ /* technically we don't need to free here, but we're nice */
free(storage); free(storage);
storage = NULL; storage = NULL;
...@@ -73,16 +81,18 @@ char * strtokx(const char *s, ...@@ -73,16 +81,18 @@ char * strtokx(const char *s,
if (quote) if (quote)
cp = strchr(quote, string[offset]); cp = strchr(quote, string[offset]);
if (cp) { if (cp)
{
/* okay, we have a quoting character, now scan for the closer */ /* okay, we have a quoting character, now scan for the closer */
char *p; char *p;
start = &string[offset+1];
start = &string[offset + 1];
if (token_pos) if (token_pos)
*token_pos = start - storage; *token_pos = start - storage;
for(p = start; for (p = start;
*p && (*p != *cp || *(p-1) == escape) ; *p && (*p != *cp || *(p - 1) == escape);
#ifdef MULTIBYTE #ifdef MULTIBYTE
p += PQmblen(p) p += PQmblen(p)
#else #else
...@@ -91,20 +101,22 @@ char * strtokx(const char *s, ...@@ -91,20 +101,22 @@ char * strtokx(const char *s,
); );
/* not yet end of string? */ /* not yet end of string? */
if (*p != '\0') { if (*p != '\0')
{
*p = '\0'; *p = '\0';
string = p + 1; string = p + 1;
if (was_quoted) if (was_quoted)
*was_quoted = *cp; *was_quoted = *cp;
unescape_quotes (start, *cp, escape); unescape_quotes(start, *cp, escape);
return start; return start;
} }
else { else
{
if (was_quoted) if (was_quoted)
*was_quoted = *cp; *was_quoted = *cp;
string = p; string = p;
unescape_quotes (start, *cp, escape); unescape_quotes(start, *cp, escape);
return start; return start;
} }
} }
...@@ -119,13 +131,15 @@ char * strtokx(const char *s, ...@@ -119,13 +131,15 @@ char * strtokx(const char *s,
if (was_quoted) if (was_quoted)
*was_quoted = 0; *was_quoted = 0;
if (start[offset] != '\0') { if (start[offset] != '\0')
{
start[offset] = '\0'; start[offset] = '\0';
string = &start[offset]+1; string = &start[offset] + 1;
return start; return start;
} }
else { else
{
string = &start[offset]; string = &start[offset];
return start; return start;
} }
...@@ -143,14 +157,16 @@ static void ...@@ -143,14 +157,16 @@ static void
unescape_quotes(char *source, char quote, char escape) unescape_quotes(char *source, char quote, char escape)
{ {
char *p; char *p;
char *destination, *tmp; char *destination,
*tmp;
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(source); assert(source);
#endif #endif
destination = (char *) calloc(1, strlen(source)+1); destination = (char *) calloc(1, strlen(source) + 1);
if (!destination) { if (!destination)
{
perror("calloc"); perror("calloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -161,15 +177,16 @@ unescape_quotes(char *source, char quote, char escape) ...@@ -161,15 +177,16 @@ unescape_quotes(char *source, char quote, char escape)
{ {
char c; char c;
if (*p == escape && *(p+1) && quote == *(p+1)) { if (*p == escape && *(p + 1) && quote == *(p + 1))
c = *(p+1); {
c = *(p + 1);
p++; p++;
} }
else else
c = *p; c = *p;
*tmp = c; *tmp = c;
tmp ++; tmp++;
} }
/* Terminating null character */ /* Terminating null character */
......
...@@ -3,12 +3,11 @@ ...@@ -3,12 +3,11 @@
/* The cooler version of strtok() which knows about quotes and doesn't /* The cooler version of strtok() which knows about quotes and doesn't
* overwrite your input */ * overwrite your input */
extern char * extern char *strtokx(const char *s,
strtokx(const char *s,
const char *delim, const char *delim,
const char *quote, const char *quote,
char escape, char escape,
char * was_quoted, char *was_quoted,
unsigned int * token_pos); unsigned int *token_pos);
#endif /* STRINGUTILS_H */ #endif /* STRINGUTILS_H */
...@@ -6,16 +6,19 @@ ...@@ -6,16 +6,19 @@
#include <assert.h> #include <assert.h>
VariableSpace CreateVariableSpace(void) VariableSpace
CreateVariableSpace(void)
{ {
struct _variable *ptr; struct _variable *ptr;
ptr = calloc(1, sizeof *ptr); ptr = calloc(1, sizeof *ptr);
if (!ptr) return NULL; if (!ptr)
return NULL;
ptr->name = strdup("@"); ptr->name = strdup("@");
ptr->value = strdup(""); ptr->value = strdup("");
if (!ptr->name || !ptr->value) { if (!ptr->name || !ptr->value)
{
free(ptr->name); free(ptr->name);
free(ptr->value); free(ptr->value);
free(ptr); free(ptr);
...@@ -27,21 +30,24 @@ VariableSpace CreateVariableSpace(void) ...@@ -27,21 +30,24 @@ VariableSpace CreateVariableSpace(void)
const char * GetVariable(VariableSpace space, const char * name) const char *
GetVariable(VariableSpace space, const char *name)
{ {
struct _variable *current; struct _variable *current;
if (!space) if (!space)
return NULL; return NULL;
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name)) return NULL; if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
return NULL;
for (current = space; current; current = current->next) { for (current = space; current; current = current->next)
{
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(current->name); assert(current->name);
assert(current->value); assert(current->value);
#endif #endif
if (strcmp(current->name, name)==0) if (strcmp(current->name, name) == 0)
return current->value; return current->value;
} }
...@@ -50,16 +56,19 @@ const char * GetVariable(VariableSpace space, const char * name) ...@@ -50,16 +56,19 @@ const char * GetVariable(VariableSpace space, const char * name)
bool GetVariableBool(VariableSpace space, const char * name) bool
GetVariableBool(VariableSpace space, const char *name)
{ {
return GetVariable(space, name)!=NULL ? true : false; return GetVariable(space, name) != NULL ? true : false;
} }
bool SetVariable(VariableSpace space, const char * name, const char * value) bool
SetVariable(VariableSpace space, const char *name, const char *value)
{ {
struct _variable *current, *previous; struct _variable *current,
*previous;
if (!space) if (!space)
return false; return false;
...@@ -67,15 +76,18 @@ bool SetVariable(VariableSpace space, const char * name, const char * value) ...@@ -67,15 +76,18 @@ bool SetVariable(VariableSpace space, const char * name, const char * value)
if (!value) if (!value)
return DeleteVariable(space, name); return DeleteVariable(space, name);
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name)) return false; if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
return false;
for (current = space; current; previous = current, current = current->next) { for (current = space; current; previous = current, current = current->next)
{
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(current->name); assert(current->name);
assert(current->value); assert(current->value);
#endif #endif
if (strcmp(current->name, name)==0) { if (strcmp(current->name, name) == 0)
free (current->value); {
free(current->value);
current->value = strdup(value); current->value = strdup(value);
return current->value ? true : false; return current->value ? true : false;
} }
...@@ -93,23 +105,28 @@ bool SetVariable(VariableSpace space, const char * name, const char * value) ...@@ -93,23 +105,28 @@ bool SetVariable(VariableSpace space, const char * name, const char * value)
bool DeleteVariable(VariableSpace space, const char * name) bool
DeleteVariable(VariableSpace space, const char *name)
{ {
struct _variable *current, *previous; struct _variable *current,
*previous;
if (!space) if (!space)
return false; return false;
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name)) return false; if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
return false;
for (current = space, previous = NULL; current; previous = current, current = current->next) { for (current = space, previous = NULL; current; previous = current, current = current->next)
{
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(current->name); assert(current->name);
assert(current->value); assert(current->value);
#endif #endif
if (strcmp(current->name, name)==0) { if (strcmp(current->name, name) == 0)
free (current->name); {
free (current->value); free(current->name);
free(current->value);
if (previous) if (previous)
previous->next = current->next; previous->next = current->next;
free(current); free(current);
...@@ -122,7 +139,8 @@ bool DeleteVariable(VariableSpace space, const char * name) ...@@ -122,7 +139,8 @@ bool DeleteVariable(VariableSpace space, const char * name)
void DestroyVariableSpace(VariableSpace space) void
DestroyVariableSpace(VariableSpace space)
{ {
if (!space) if (!space)
return; return;
......
...@@ -13,20 +13,21 @@ ...@@ -13,20 +13,21 @@
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_" #define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
struct _variable { struct _variable
char * name; {
char * value; char *name;
struct _variable * next; char *value;
struct _variable *next;
}; };
typedef struct _variable * VariableSpace; typedef struct _variable *VariableSpace;
VariableSpace CreateVariableSpace(void); VariableSpace CreateVariableSpace(void);
const char * GetVariable(VariableSpace space, const char * name); const char *GetVariable(VariableSpace space, const char *name);
bool GetVariableBool(VariableSpace space, const char * name); bool GetVariableBool(VariableSpace space, const char *name);
bool SetVariable(VariableSpace space, const char * name, const char * value); bool SetVariable(VariableSpace space, const char *name, const char *value);
bool DeleteVariable(VariableSpace space, const char * name); bool DeleteVariable(VariableSpace space, const char *name);
void DestroyVariableSpace(VariableSpace space); void DestroyVariableSpace(VariableSpace space);
......
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