1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-23 14:01:44 +03:00

psql cleanup

This commit is contained in:
Bruce Momjian
1999-11-04 23:14:30 +00:00
parent 2323b63631
commit 0e6652e673
29 changed files with 5255 additions and 8040 deletions

File diff suppressed because it is too large Load Diff

View File

@ -11,39 +11,36 @@
typedef enum _backslashResult { typedef enum _backslashResult
CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */ {
CMD_SEND, /* query complete; send off */ CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */
CMD_SKIP_LINE, /* keep building query */ CMD_SEND, /* query complete; send off */
CMD_TERMINATE, /* quit program */ CMD_SKIP_LINE, /* keep building query */
CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */ CMD_TERMINATE, /* quit program */
CMD_ERROR /* the execution of the backslash command resulted CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */
in an error */ CMD_ERROR /* the execution of the backslash command
} backslashResult; * resulted in an error */
} 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);
#endif #endif

View File

@ -14,7 +14,7 @@
#include <signal.h> #include <signal.h>
#include <assert.h> #include <assert.h>
#ifndef WIN32 #ifndef WIN32
#include <unistd.h> /* for write() */ #include <unistd.h> /* for write() */
#endif #endif
#include <libpq-fe.h> #include <libpq-fe.h>
@ -39,19 +39,23 @@
* "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) {
fprintf(stderr, "xstrdup: Cannot duplicate null pointer.\n"); if (!string)
exit(EXIT_FAILURE); {
} fprintf(stderr, "xstrdup: Cannot duplicate null pointer.\n");
tmp = strdup(string); exit(EXIT_FAILURE);
if (!tmp) { }
perror("strdup"); tmp = strdup(string);
exit(EXIT_FAILURE); if (!tmp)
} {
return tmp; perror("strdup");
exit(EXIT_FAILURE);
}
return tmp;
} }
@ -67,66 +71,67 @@ char * xstrdup(const char * string)
bool bool
setQFout(const char *fname, PsqlSettings *pset) setQFout(const char *fname, PsqlSettings *pset)
{ {
bool status = true; bool status = true;
#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 */
if (pset->queryFout && pset->queryFout != stdout && pset->queryFout != stderr) if (pset->queryFout && pset->queryFout != stdout && pset->queryFout != stderr)
{ {
if (pset->queryFoutPipe) if (pset->queryFoutPipe)
pclose(pset->queryFout); pclose(pset->queryFout);
else
fclose(pset->queryFout);
}
/* If no filename, set stdout */
if (!fname || fname[0] == '\0')
{
pset->queryFout = stdout;
pset->queryFoutPipe = false;
}
else if (*fname == '|')
{
const char *pipename = fname + 1;
#ifndef __CYGWIN32__
pset->queryFout = popen(pipename, "w");
#else
pset->queryFout = popen(pipename, "wb");
#endif
pset->queryFoutPipe = true;
}
else else
fclose(pset->queryFout); {
}
/* If no filename, set stdout */
if (!fname || fname[0]=='\0')
{
pset->queryFout = stdout;
pset->queryFoutPipe = false;
}
else if (*fname == '|')
{
const char * pipename = fname+1;
#ifndef __CYGWIN32__ #ifndef __CYGWIN32__
pset->queryFout = popen(pipename, "w"); pset->queryFout = fopen(fname, "w");
#else #else
pset->queryFout = popen(pipename, "wb"); pset->queryFout = fopen(fname, "wb");
#endif #endif
pset->queryFoutPipe = true; pset->queryFoutPipe = false;
} }
else
{
#ifndef __CYGWIN32__
pset->queryFout = fopen(fname, "w");
#else
pset->queryFout = fopen(fname, "wb");
#endif
pset->queryFoutPipe = false;
}
if (!pset->queryFout) if (!pset->queryFout)
{ {
perror(fname); perror(fname);
pset->queryFout = stdout; pset->queryFout = stdout;
pset->queryFoutPipe = false; pset->queryFoutPipe = false;
status = false; status = false;
} }
/* Direct signals */ /* Direct signals */
if (pset->queryFoutPipe) if (pset->queryFoutPipe)
pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN);
else else
pqsignal(SIGPIPE, SIG_DFL); pqsignal(SIGPIPE, SIG_DFL);
return status; return status;
} }
@ -137,60 +142,67 @@ setQFout(const char *fname, PsqlSettings *pset)
* Generalized function especially intended for reading in usernames and * Generalized function especially intended for reading in usernames and
* password interactively. Reads from stdin. * password interactively. Reads from stdin.
* *
* prompt: The prompt to print * prompt: The prompt to print
* maxlen: How many characters to accept * maxlen: How many characters to accept
* echo: Set to false if you want to hide what is entered (for passwords) * echo: Set to false if you want to hide what is entered (for passwords)
* *
* Returns a malloc()'ed string with the input (w/o trailing newline). * Returns a malloc()'ed string with the input (w/o trailing newline).
*/ */
char * 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)
{ {
tcgetattr(0, &t); tcgetattr(0, &t);
t_orig = t; t_orig = t;
t.c_lflag &= ~ECHO; t.c_lflag &= ~ECHO;
tcsetattr(0, TCSADRAIN, &t); tcsetattr(0, TCSADRAIN, &t);
} }
#endif #endif
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); {
puts(""); tcsetattr(0, TCSADRAIN, &t_orig);
} 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 */ {
char buf[512]; /* eat rest of the line */
do { char buf[512];
fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
if (length > 0 && destination[length - 1] == '\n') do
/* remove trailing newline */ {
destination[length - 1] = '\0'; fgets(buf, 512, stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
return destination; if (length > 0 && destination[length - 1] == '\n')
/* remove trailing newline */
destination[length - 1] = '\0';
return destination;
} }
@ -205,66 +217,77 @@ 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); {
if (var) var = GetVariable(pset->vars, name);
return var; if (var)
else return var;
return ""; else
} return "";
}
/* 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)
return PG_VERSION_STR;
if (strcmp(name, "Database")==0) { /*
if (PQdb(pset->db)) * (by convention these should be capitalized (but not all caps), to
return PQdb(pset->db); * not be shadowed by regular vars or to shadow env vars)
else */
return ""; if (strcmp(name, "Version") == 0)
} return PG_VERSION_STR;
if (strcmp(name, "User")==0) { if (strcmp(name, "Database") == 0)
if (PQuser(pset->db)) {
return PQuser(pset->db); if (PQdb(pset->db))
else return PQdb(pset->db);
return ""; else
} return "";
}
if (strcmp(name, "Host")==0) { if (strcmp(name, "User") == 0)
if (PQhost(pset->db)) {
return PQhost(pset->db); if (PQuser(pset->db))
else return PQuser(pset->db);
return ""; else
} return "";
}
if (strcmp(name, "Port")==0) { if (strcmp(name, "Host") == 0)
if (PQport(pset->db)) {
return PQport(pset->db); if (PQhost(pset->db))
else return PQhost(pset->db);
return ""; else
} return "";
}
/* env vars (if env vars are all caps there should be no prob, otherwise if (strcmp(name, "Port") == 0)
you're on your own */ {
if (PQport(pset->db))
return PQport(pset->db);
else
return "";
}
if ((var = getenv(name))) /*
return var; * env vars (if env vars are all caps there should be no prob,
* otherwise you're on your own
*/
return ""; if ((var = getenv(name)))
return var;
return "";
} }
@ -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)
@ -296,16 +319,17 @@ PGconn * cancelConn;
static void static void
handle_sigint(SIGNAL_ARGS) handle_sigint(SIGNAL_ARGS)
{ {
/* accept signal if no connection */ /* accept signal if no connection */
if (cancelConn == NULL) if (cancelConn == NULL)
exit(1); exit(1);
/* 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(PQerrorMessage(cancelConn)); safe_write_stderr("\nCould not send cancel request: ");
} safe_write_stderr(PQerrorMessage(cancelConn));
}
} }
@ -316,59 +340,63 @@ handle_sigint(SIGNAL_ARGS)
* This is the way to send "backdoor" queries (those not directly entered * This is the way to send "backdoor" queries (those not directly entered
* by the user). It is subject to -E (echo_secret) but not -e (echo). * by the user). It is subject to -E (echo_secret) but not -e (echo).
*/ */
PGresult * 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); {
return NULL; fputs("You are not currently connected to a database.\n", stderr);
} return NULL;
var = GetVariable(pset->vars, "echo_secret");
if (var) {
printf("********* QUERY *********\n%s\n*************************\n\n", query);
fflush(stdout);
}
if (var && strcmp(var, "noexec")==0)
return NULL;
cancelConn = pset->db;
pqsignal(SIGINT, handle_sigint); /* control-C => cancel */
res = PQexec(pset->db, query);
pqsignal(SIGINT, SIG_DFL); /* no control-C is back to normal */
if (PQstatus(pset->db) == CONNECTION_BAD)
{
fputs("The connection to the server was lost. Attempting reset: ", stderr);
PQreset(pset->db);
if (PQstatus(pset->db) == CONNECTION_BAD) {
fputs("Failed.\n", stderr);
PQfinish(pset->db);
PQclear(res);
pset->db = NULL;
return NULL;
} }
else
fputs("Succeeded.\n", stderr);
}
if (res && (PQresultStatus(res) == PGRES_COMMAND_OK || var = GetVariable(pset->vars, "echo_secret");
PQresultStatus(res) == PGRES_TUPLES_OK || if (var)
PQresultStatus(res) == PGRES_COPY_IN || {
PQresultStatus(res) == PGRES_COPY_OUT) printf("********* QUERY *********\n%s\n*************************\n\n", query);
) fflush(stdout);
return res; }
else {
fprintf(stderr, "%s", PQerrorMessage(pset->db)); if (var && strcmp(var, "noexec") == 0)
PQclear(res); return NULL;
return NULL;
} cancelConn = pset->db;
pqsignal(SIGINT, handle_sigint); /* control-C => cancel */
res = PQexec(pset->db, query);
pqsignal(SIGINT, SIG_DFL); /* no control-C is back to normal */
if (PQstatus(pset->db) == CONNECTION_BAD)
{
fputs("The connection to the server was lost. Attempting reset: ", stderr);
PQreset(pset->db);
if (PQstatus(pset->db) == CONNECTION_BAD)
{
fputs("Failed.\n", stderr);
PQfinish(pset->db);
PQclear(res);
pset->db = NULL;
return NULL;
}
else
fputs("Succeeded.\n", stderr);
}
if (res && (PQresultStatus(res) == PGRES_COMMAND_OK ||
PQresultStatus(res) == PGRES_TUPLES_OK ||
PQresultStatus(res) == PGRES_COPY_IN ||
PQresultStatus(res) == PGRES_COPY_OUT)
)
return res;
else
{
fprintf(stderr, "%s", PQerrorMessage(pset->db));
PQclear(res);
return NULL;
}
} }
@ -388,131 +416,136 @@ PSQLexec(PsqlSettings *pset, const char *query)
bool bool
SendQuery(PsqlSettings *pset, const char *query) SendQuery(PsqlSettings *pset, const char *query)
{ {
bool success = false; bool success = false;
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);
return false;
}
if (GetVariableBool(pset->vars, "singlestep")) {
char buf[3];
fprintf(stdout, "***(Single step mode: Verify query)*********************************************\n"
"QUERY: %s\n"
"***(press return to proceed or enter x and return to cancel)********************\n",
query);
fflush(stdout);
fgets(buf, 3, stdin);
if (buf[0]=='x')
return false;
fflush(stdin);
}
cancelConn = pset->db;
pqsignal(SIGINT, handle_sigint);
results = PQexec(pset->db, query);
pqsignal(SIGINT, SIG_DFL);
if (results == NULL)
{
fputs(PQerrorMessage(pset->db), pset->queryFout);
success = false;
}
else
{
switch (PQresultStatus(results))
{ {
case PGRES_TUPLES_OK: fputs("You are not currently connected to a database.\n", stderr);
if (pset->gfname) return false;
{ }
PsqlSettings settings_copy = *pset;
settings_copy.queryFout = stdout; if (GetVariableBool(pset->vars, "singlestep"))
if (!setQFout(pset->gfname, &settings_copy)) { {
success = false; char buf[3];
break;
fprintf(stdout, "***(Single step mode: Verify query)*********************************************\n"
"QUERY: %s\n"
"***(press return to proceed or enter x and return to cancel)********************\n",
query);
fflush(stdout);
fgets(buf, 3, stdin);
if (buf[0] == 'x')
return false;
fflush(stdin);
}
cancelConn = pset->db;
pqsignal(SIGINT, handle_sigint);
results = PQexec(pset->db, query);
pqsignal(SIGINT, SIG_DFL);
if (results == NULL)
{
fputs(PQerrorMessage(pset->db), pset->queryFout);
success = false;
}
else
{
switch (PQresultStatus(results))
{
case PGRES_TUPLES_OK:
if (pset->gfname)
{
PsqlSettings settings_copy = *pset;
settings_copy.queryFout = stdout;
if (!setQFout(pset->gfname, &settings_copy))
{
success = false;
break;
}
printQuery(results, &settings_copy.popt, settings_copy.queryFout);
/* close file/pipe */
setQFout(NULL, &settings_copy);
free(pset->gfname);
pset->gfname = NULL;
success = true;
break;
}
else
{
success = true;
printQuery(results, &pset->popt, pset->queryFout);
fflush(pset->queryFout);
}
break;
case PGRES_EMPTY_QUERY:
success = true;
break;
case PGRES_COMMAND_OK:
success = true;
fprintf(pset->queryFout, "%s\n", PQcmdStatus(results));
break;
case PGRES_COPY_OUT:
if (pset->cur_cmd_interactive && !GetVariable(pset->vars, "quiet"))
puts("Copy command returns:");
success = handleCopyOut(pset->db, pset->queryFout);
break;
case PGRES_COPY_IN:
if (pset->cur_cmd_interactive && !GetVariable(pset->vars, "quiet"))
puts("Enter data to be copied followed by a newline.\n"
"End with a backslash and a period on a line by itself.");
success = handleCopyIn(pset->db, pset->cur_cmd_source,
pset->cur_cmd_interactive ? get_prompt(pset, PROMPT_COPY) : NULL);
break;
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE:
success = false;
fputs(PQerrorMessage(pset->db), pset->queryFout);
break;
} }
printQuery(results, &settings_copy.popt, settings_copy.queryFout); if (PQstatus(pset->db) == CONNECTION_BAD)
{
fputs("The connection to the server was lost. Attempting reset: ", stderr);
PQreset(pset->db);
if (PQstatus(pset->db) == CONNECTION_BAD)
{
fputs("Failed.\n", stderr);
PQfinish(pset->db);
PQclear(results);
pset->db = NULL;
return false;
}
else
fputs("Succeeded.\n", stderr);
}
/* close file/pipe */ /* check for asynchronous notification returns */
setQFout(NULL, &settings_copy); while ((notify = PQnotifies(pset->db)) != NULL)
{
fprintf(pset->queryFout, "Asynchronous NOTIFY '%s' from backend with pid '%d' received.\n",
notify->relname, notify->be_pid);
free(notify);
}
free(pset->gfname); if (results)
pset->gfname = NULL; PQclear(results);
success = true;
break;
}
else
{
success = true;
printQuery(results, &pset->popt, pset->queryFout);
fflush(pset->queryFout);
}
break;
case PGRES_EMPTY_QUERY:
success = true;
break;
case PGRES_COMMAND_OK:
success = true;
fprintf(pset->queryFout, "%s\n", PQcmdStatus(results));
break;
case PGRES_COPY_OUT:
if (pset->cur_cmd_interactive && !GetVariable(pset->vars, "quiet"))
puts("Copy command returns:");
success = handleCopyOut(pset->db, pset->queryFout);
break;
case PGRES_COPY_IN:
if (pset->cur_cmd_interactive && !GetVariable(pset->vars, "quiet"))
puts("Enter data to be copied followed by a newline.\n"
"End with a backslash and a period on a line by itself.");
success = handleCopyIn(pset->db, pset->cur_cmd_source,
pset->cur_cmd_interactive ? get_prompt(pset, PROMPT_COPY) : NULL);
break;
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE:
success = false;
fputs(PQerrorMessage(pset->db), pset->queryFout);
break;
} }
if (PQstatus(pset->db) == CONNECTION_BAD) return success;
{
fputs("The connection to the server was lost. Attempting reset: ", stderr);
PQreset(pset->db);
if (PQstatus(pset->db) == CONNECTION_BAD) {
fputs("Failed.\n", stderr);
PQfinish(pset->db);
PQclear(results);
pset->db = NULL;
return false;
}
else
fputs("Succeeded.\n", stderr);
}
/* check for asynchronous notification returns */
while ((notify = PQnotifies(pset->db)) != NULL)
{
fprintf(pset->queryFout, "Asynchronous NOTIFY '%s' from backend with pid '%d' received.\n",
notify->relname, notify->be_pid);
free(notify);
}
if (results)
PQclear(results);
}
return success;
} }

View File

@ -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 */

View File

@ -8,9 +8,9 @@
#include <errno.h> #include <errno.h>
#include <assert.h> #include <assert.h>
#ifndef WIN32 #ifndef WIN32
#include <unistd.h> /* for isatty */ #include <unistd.h> /* for isatty */
#else #else
#include <io.h> /* I think */ #include <io.h> /* I think */
#endif #endif
#include <libpq-fe.h> #include <libpq-fe.h>
@ -33,136 +33,150 @@
* 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;
bool from; char *file;
bool binary; bool from;
bool oids; bool binary;
char * delim; bool oids;
char *delim;
}; };
static void static void
free_copy_options(struct copy_options * ptr) free_copy_options(struct copy_options * ptr)
{ {
if (!ptr) if (!ptr)
return; return;
free(ptr->table); free(ptr->table);
free(ptr->file); free(ptr->file);
free(ptr->delim); free(ptr->delim);
free(ptr); free(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"); {
exit(EXIT_FAILURE); perror("calloc");
} exit(EXIT_FAILURE);
}
token = strtokx(line, " \t", "\"", '\\', &quote, NULL);
if (!token) token = strtokx(line, " \t", "\"", '\\', &quote, NULL);
error = true; if (!token)
else { error = true;
if (!quote && strcasecmp(token, "binary")==0) { else
result->binary = true; {
token = strtokx(NULL, " \t", "\"", '\\', &quote, NULL); if (!quote && strcasecmp(token, "binary") == 0)
if (!token) {
error = true; result->binary = true;
token = strtokx(NULL, " \t", "\"", '\\', &quote, NULL);
if (!token)
error = true;
}
if (token)
result->table = xstrdup(token);
} }
if (token)
result->table = xstrdup(token);
}
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
assert(error || result->table); assert(error || result->table);
#endif #endif
if (!error) { if (!error)
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); {
if (!token)
error = true;
else {
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)
error = true; error = true;
else else
result->oids = true; {
if (strcasecmp(token, "with") == 0)
{
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token || strcasecmp(token, "oids") != 0)
error = true;
else
result->oids = true;
if (!error) { if (!error)
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); {
if (!token) token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
error = true; if (!token)
error = true;
}
}
if (!error && strcasecmp(token, "from") == 0)
result->from = true;
else if (!error && strcasecmp(token, "to") == 0)
result->from = false;
else
error = true;
} }
}
if (!error && strcasecmp(token, "from")==0)
result->from = true;
else if (!error && strcasecmp(token, "to")==0)
result->from = false;
else
error = true;
} }
}
if (!error) { if (!error)
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL); {
if (!token) token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
error = true; if (!token)
else error = true;
result->file=xstrdup(token); else
} 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); {
if (token) {
if (strcasecmp(token, "using")!=0)
error = true;
else {
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL); token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
if (!token || strcasecmp(token, "delimiters")!=0) if (token)
error = true; {
else { if (strcasecmp(token, "using") != 0)
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL); error = true;
if (token) else
result->delim = xstrdup(token); {
else token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
error = true; if (!token || strcasecmp(token, "delimiters") != 0)
error = true;
else
{
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
if (token)
result->delim = xstrdup(token);
else
error = true;
}
}
} }
}
} }
}
free(line); free(line);
if (error) { if (error)
fputs("Parse error at ", stderr); {
if (!token) fputs("Parse error at ", stderr);
fputs("end of line.", stderr); if (!token)
fputs("end of line.", stderr);
else
fprintf(stderr, "'%s'.", token);
fputs("\n", stderr);
free(result);
return NULL;
}
else else
fprintf(stderr, "'%s'.", token); return result;
fputs("\n", stderr);
free(result);
return NULL;
}
else
return result;
} }
@ -173,101 +187,107 @@ 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;
struct copy_options *options; struct copy_options *options;
PGresult *result; PGresult *result;
bool success; bool success;
/* parse options */ /* parse options */
options = parse_slash_copy(args); options = parse_slash_copy(args);
if (!options) if (!options)
return false; return false;
strcpy(query, "COPY "); strcpy(query, "COPY ");
if (options->binary) if (options->binary)
fputs("Warning: \\copy binary is not implemented. Resorting to text output.\n", stderr); fputs("Warning: \\copy binary is not implemented. Resorting to text output.\n", stderr);
/* strcat(query, "BINARY "); */ /* strcat(query, "BINARY "); */
strcat(query, "\""); strcat(query, "\"");
strncat(query, options->table, NAMEDATALEN); strncat(query, options->table, NAMEDATALEN);
strcat(query, "\" "); strcat(query, "\" ");
if (options->oids) if (options->oids)
strcat(query, "WITH OIDS "); strcat(query, "WITH OIDS ");
if (options->from) if (options->from)
strcat(query, "FROM stdin"); strcat(query, "FROM stdin");
else
strcat(query, "TO stdout");
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) */
strncat(query, " USING DELIMITERS '", 2);
strcat(query, options->delim);
strcat(query, "'");
}
if (options->from)
#ifndef __CYGWIN32__
copystream = fopen(options->file, "r");
#else
copystream = fopen(options->file, "rb");
#endif
else
#ifndef __CYGWIN32__
copystream = fopen(options->file, "w");
#else
copystream = fopen(options->file, "wb");
#endif
if (!copystream) {
fprintf(stderr,
"Unable to open file %s which to copy: %s\n",
options->from ? "from" : "to", strerror(errno));
free_copy_options(options);
return false;
}
result = PSQLexec(pset, query);
switch (PQresultStatus(result))
{
case PGRES_COPY_OUT:
success = handleCopyOut(pset->db, copystream);
break;
case PGRES_COPY_IN:
success = handleCopyIn(pset->db, copystream, NULL);
break;
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE:
success = false;
fputs(PQerrorMessage(pset->db), stderr);
break;
default:
success = false;
fprintf(stderr, "Unexpected response (%d)\n", PQresultStatus(result));
}
PQclear(result);
if (!GetVariable(pset->vars, "quiet")) {
if (success)
puts("Successfully copied.");
else else
puts("Copy failed."); strcat(query, "TO stdout");
}
fclose(copystream);
free_copy_options(options); if (options->delim)
return success; {
/*
* 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);
strcat(query, options->delim);
strcat(query, "'");
}
if (options->from)
#ifndef __CYGWIN32__
copystream = fopen(options->file, "r");
#else
copystream = fopen(options->file, "rb");
#endif
else
#ifndef __CYGWIN32__
copystream = fopen(options->file, "w");
#else
copystream = fopen(options->file, "wb");
#endif
if (!copystream)
{
fprintf(stderr,
"Unable to open file %s which to copy: %s\n",
options->from ? "from" : "to", strerror(errno));
free_copy_options(options);
return false;
}
result = PSQLexec(pset, query);
switch (PQresultStatus(result))
{
case PGRES_COPY_OUT:
success = handleCopyOut(pset->db, copystream);
break;
case PGRES_COPY_IN:
success = handleCopyIn(pset->db, copystream, NULL);
break;
case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE:
success = false;
fputs(PQerrorMessage(pset->db), stderr);
break;
default:
success = false;
fprintf(stderr, "Unexpected response (%d)\n", PQresultStatus(result));
}
PQclear(result);
if (!GetVariable(pset->vars, "quiet"))
{
if (success)
puts("Successfully copied.");
else
puts("Copy failed.");
}
fclose(copystream);
free_copy_options(options);
return success;
} }
@ -287,38 +307,38 @@ do_copy(const char * args, PsqlSettings *pset)
bool bool
handleCopyOut(PGconn *conn, FILE *copystream) handleCopyOut(PGconn *conn, FILE *copystream)
{ {
bool copydone = false; /* haven't started yet */ bool copydone = false; /* haven't started yet */
char copybuf[COPYBUFSIZ]; char copybuf[COPYBUFSIZ];
int ret; int ret;
while (!copydone) while (!copydone)
{ {
ret = PQgetline(conn, copybuf, COPYBUFSIZ); ret = PQgetline(conn, copybuf, COPYBUFSIZ);
if (copybuf[0] == '\\' && if (copybuf[0] == '\\' &&
copybuf[1] == '.' && copybuf[1] == '.' &&
copybuf[2] == '\0') copybuf[2] == '\0')
{ {
copydone = true; /* we're at the end */ copydone = true; /* we're at the end */
}
else
{
fputs(copybuf, copystream);
switch (ret)
{
case EOF:
copydone = true;
/* FALLTHROUGH */
case 0:
fputc('\n', copystream);
break;
case 1:
break;
}
}
} }
else fflush(copystream);
{ return !PQendcopy(conn);
fputs(copybuf, copystream);
switch (ret)
{
case EOF:
copydone = true;
/* FALLTHROUGH */
case 0:
fputc('\n', copystream);
break;
case 1:
break;
}
}
}
fflush(copystream);
return !PQendcopy(conn);
} }
@ -333,58 +353,58 @@ handleCopyOut(PGconn *conn, FILE *copystream)
* (and which gave you PGRES_COPY_IN back); * (and which gave you PGRES_COPY_IN back);
* copystream is the file stream you want the input to come from * copystream is the file stream you want the input to come from
* prompt is something to display to request user input (only makes sense * prompt is something to display to request user input (only makes sense
* if stdin is an interactive tty) * if stdin is an interactive tty)
*/ */
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;
bool linedone; bool linedone;
char copybuf[COPYBUFSIZ]; char copybuf[COPYBUFSIZ];
char *s; char *s;
int buflen; int buflen;
int c = 0; int c = 0;
while (!copydone) while (!copydone)
{ /* for each input line ... */ { /* for each input line ... */
if (prompt && isatty(fileno(stdin))) if (prompt && isatty(fileno(stdin)))
{
fputs(prompt, 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; fputs(prompt, stdout);
break; fflush(stdout);
} }
*s++ = c; firstload = true;
} linedone = false;
*s = '\0'; while (!linedone)
if (c == EOF) { /* for each buffer ... */
{ s = copybuf;
PQputline(conn, "\\."); for (buflen = COPYBUFSIZ; buflen > 1; buflen--)
copydone = true; {
break; c = getc(copystream);
} if (c == '\n' || c == EOF)
PQputline(conn, copybuf); {
if (firstload) linedone = true;
{ break;
if (!strcmp(copybuf, "\\.")) }
copydone = true; *s++ = c;
firstload = false; }
} *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");
} }
PQputline(conn, "\n"); return !PQendcopy(conn);
}
return !PQendcopy(conn);
} }

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -7,12 +7,12 @@
#include <signal.h> #include <signal.h>
#ifndef WIN32 #ifndef WIN32
#include <sys/ioctl.h> /* for ioctl() */ #include <sys/ioctl.h> /* for ioctl() */
#ifdef HAVE_PWD_H #ifdef HAVE_PWD_H
#include <pwd.h> /* for getpwuid() */ #include <pwd.h> /* for getpwuid() */
#endif #endif
#include <sys/types.h> /* (ditto) */ #include <sys/types.h> /* (ditto) */
#include <unistd.h> /* for getuid() */ #include <unistd.h> /* for getuid() */
#else #else
#define strcasecmp(x,y) stricmp(x,y) #define strcasecmp(x,y) stricmp(x,y)
#define popen(x,y) _popen(x,y) #define popen(x,y) _popen(x,y)
@ -34,85 +34,94 @@
*/ */
#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;
perror("getpwuid()"); else
exit(EXIT_FAILURE); {
} perror("getpwuid()");
exit(EXIT_FAILURE);
}
#else #else
user = "?"; user = "?";
#endif #endif
} }
/* If string begins " here, then it ought to end there to fit on an 80 column terminal> > > > > > > " */ /* If string begins " here, then it ought to end there to fit on an 80 column terminal> > > > > > > " */
fprintf(stderr, "Usage: psql [options] [dbname [username]] \n"); fprintf(stderr, "Usage: psql [options] [dbname [username]] \n");
fprintf(stderr, " -A Unaligned table output mode (-P format=unaligned)\n"); fprintf(stderr, " -A Unaligned table output mode (-P format=unaligned)\n");
fprintf(stderr, " -c query Run single query (slash commands, too) and exit\n"); fprintf(stderr, " -c query Run single query (slash commands, too) and exit\n");
/* Display default database */ /* Display default database */
env = getenv("PGDATABASE"); env = getenv("PGDATABASE");
if (!env) env=user; if (!env)
fprintf(stderr, " -d dbname Specify database name to connect to (default: %s)\n", env); env = user;
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");
fprintf(stderr, " -E Display queries that internal commands generate\n"); fprintf(stderr, " -E Display queries that internal commands generate\n");
fprintf(stderr, " -f filename Execute queries from file, then exit\n"); fprintf(stderr, " -f filename Execute queries from file, then exit\n");
fprintf(stderr, " -F sep Set field separator (default: '" DEFAULT_FIELD_SEP "') (-P fieldsep=)\n"); fprintf(stderr, " -F sep Set field separator (default: '" DEFAULT_FIELD_SEP "') (-P fieldsep=)\n");
/* Display default host */ /* Display default host */
env = getenv("PGHOST"); env = getenv("PGHOST");
fprintf(stderr, " -h host Specify database server host (default: "); fprintf(stderr, " -h host Specify database server host (default: ");
if (env) if (env)
fprintf(stderr, env); fprintf(stderr, env);
else else
fprintf(stderr, "domain socket"); fprintf(stderr, "domain socket");
fprintf(stderr, ")\n"); fprintf(stderr, ")\n");
fprintf(stderr, " -H HTML table output mode (-P format=html)\n"); fprintf(stderr, " -H HTML table output mode (-P format=html)\n");
fprintf(stderr, " -l List available databases, then exit\n"); fprintf(stderr, " -l List available databases, then exit\n");
fprintf(stderr, " -n Do not use readline and history\n"); fprintf(stderr, " -n Do not use readline and history\n");
fprintf(stderr, " -o filename Send query output to filename (or |pipe)\n"); fprintf(stderr, " -o filename Send query output to filename (or |pipe)\n");
/* Display default port */ /* Display default port */
env = getenv("PGPORT"); env = getenv("PGPORT");
fprintf(stderr, " -p port Specify database server port (default: %s)\n", fprintf(stderr, " -p port Specify database server port (default: %s)\n",
env ? env : "hardwired"); env ? env : "hardwired");
fprintf(stderr, " -P var[=arg] Set printing option 'var' to 'arg'. (see \\pset command)\n"); fprintf(stderr, " -P var[=arg] Set printing option 'var' to 'arg'. (see \\pset command)\n");
fprintf(stderr, " -q Run quietly (no messages, no prompts)\n"); fprintf(stderr, " -q Run quietly (no messages, no prompts)\n");
fprintf(stderr, " -s Single step mode (confirm each query)\n"); fprintf(stderr, " -s Single step mode (confirm each query)\n");
fprintf(stderr, " -S Single line mode (newline sends query)\n"); fprintf(stderr, " -S Single line mode (newline sends query)\n");
fprintf(stderr, " -t Don't print headings and row count (-P tuples_only)\n"); fprintf(stderr, " -t Don't print headings and row count (-P tuples_only)\n");
fprintf(stderr, " -T text Set HTML table tag options (e.g., width, border)\n"); fprintf(stderr, " -T text Set HTML table tag options (e.g., width, border)\n");
fprintf(stderr, " -u Prompt for username and password (same as \"-U ? -W\")\n"); fprintf(stderr, " -u Prompt for username and password (same as \"-U ? -W\")\n");
/* Display default user */ /* Display default user */
env = getenv("PGUSER"); env = getenv("PGUSER");
if (!env) env=user; if (!env)
fprintf(stderr, " -U [username] Specifiy username, \"?\"=prompt (default user: %s)\n", env); env = user;
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");
fprintf(stderr, " -v name=val Set psql variable 'name' to 'value'\n"); fprintf(stderr, " -v name=val Set psql variable 'name' to 'value'\n");
fprintf(stderr, " -V Show version information and exit\n"); fprintf(stderr, " -V Show version information and exit\n");
fprintf(stderr, " -W Prompt for password (should happen automatically)\n"); fprintf(stderr, " -W Prompt for password (should happen automatically)\n");
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,82 +134,85 @@ void usage(void)
*/ */
#ifndef TIOCGWINSZ #ifndef TIOCGWINSZ
struct winsize { struct winsize
int ws_row; {
int ws_col; int ws_row;
int ws_col;
}; };
#endif #endif
void void
slashUsage(PsqlSettings *pset) slashUsage(PsqlSettings *pset)
{ {
bool usePipe = false; bool usePipe = false;
const char *pagerenv; const char *pagerenv;
FILE *fout; FILE *fout;
struct winsize screen_size; struct winsize screen_size;
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
if (pset->notty == 0 && if (pset->notty == 0 &&
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 || (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
screen_size.ws_col == 0 || screen_size.ws_col == 0 ||
screen_size.ws_row == 0)) screen_size.ws_row == 0))
{ {
#endif #endif
screen_size.ws_row = 24; screen_size.ws_row = 24;
screen_size.ws_col = 80; screen_size.ws_col = 80;
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
} }
#endif #endif
if (pset->notty == 0 && if (pset->notty == 0 &&
(pagerenv = getenv("PAGER")) && (pagerenv = getenv("PAGER")) &&
(pagerenv[0] != '\0') && (pagerenv[0] != '\0') &&
screen_size.ws_row <= 36 && screen_size.ws_row <= 36 &&
(fout = popen(pagerenv, "w"))) (fout = popen(pagerenv, "w")))
{ {
usePipe = true; usePipe = true;
pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN);
} }
else else
fout = stdout; fout = stdout;
/* if you add/remove a line here, change the row test above */ /* if you add/remove a line here, change the row test above */
fprintf(fout, " \\? -- help\n"); fprintf(fout, " \\? -- help\n");
fprintf(fout, " \\c[onnect] [<dbname>|- [<user>|?]] -- connect to new database (currently '%s')\n", PQdb(pset->db)); fprintf(fout, " \\c[onnect] [<dbname>|- [<user>|?]] -- connect to new database (currently '%s')\n", PQdb(pset->db));
fprintf(fout, " \\copy [binary] <table> [with oids] {from|to} <fname> [with delimiters '<char>']\n"); fprintf(fout, " \\copy [binary] <table> [with oids] {from|to} <fname> [with delimiters '<char>']\n");
fprintf(fout, " \\copyright -- show PostgreSQL copyright\n"); fprintf(fout, " \\copyright -- show PostgreSQL copyright\n");
fprintf(fout, " \\d -- list tables, views, and sequences\n"); fprintf(fout, " \\d -- list tables, views, and sequences\n");
fprintf(fout, " \\distvS -- list only indices/sequences/tables/views/system tables\n"); fprintf(fout, " \\distvS -- list only indices/sequences/tables/views/system tables\n");
fprintf(fout, " \\da -- list aggregates\n"); fprintf(fout, " \\da -- list aggregates\n");
fprintf(fout, " \\dd [<object>]- list comment for table, type, function, or operator\n"); fprintf(fout, " \\dd [<object>]- list comment for table, type, function, or operator\n");
fprintf(fout, " \\df -- list functions\n"); fprintf(fout, " \\df -- list functions\n");
fprintf(fout, " \\do -- list operators\n"); fprintf(fout, " \\do -- list operators\n");
fprintf(fout, " \\dT -- list data types\n"); fprintf(fout, " \\dT -- list data types\n");
fprintf(fout, " \\e [<fname>] -- edit the current query buffer or <fname> with external editor\n"); fprintf(fout, " \\e [<fname>] -- edit the current query buffer or <fname> with external editor\n");
fprintf(fout, " \\echo <text> -- write text to stdout\n"); fprintf(fout, " \\echo <text> -- write text to stdout\n");
fprintf(fout, " \\g [<fname>] -- send query to backend (and results in <fname> or |pipe)\n"); fprintf(fout, " \\g [<fname>] -- 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 [<cmd>] -- help on syntax of sql commands, * for all commands\n");
fprintf(fout, " \\i <fname> -- read and execute queries from filename\n"); fprintf(fout, " \\i <fname> -- read and execute queries from filename\n");
fprintf(fout, " \\l -- list all databases\n"); fprintf(fout, " \\l -- list all databases\n");
fprintf(fout, " \\lo_export, \\lo_import, \\lo_list, \\lo_unlink -- large object operations\n"); fprintf(fout, " \\lo_export, \\lo_import, \\lo_list, \\lo_unlink -- large object operations\n");
fprintf(fout, " \\o [<fname>] -- send all query results to <fname>, or |pipe\n"); fprintf(fout, " \\o [<fname>] -- send all query results to <fname>, or |pipe\n");
fprintf(fout, " \\p -- print the content of the current query buffer\n"); fprintf(fout, " \\p -- print the content of the current query buffer\n");
fprintf(fout, " \\pset -- set table output options\n"); fprintf(fout, " \\pset -- set table output options\n");
fprintf(fout, " \\q -- quit\n"); fprintf(fout, " \\q -- quit\n");
fprintf(fout, " \\qecho <text>-- write text to query output stream (see \\o)\n"); fprintf(fout, " \\qecho <text>-- write text to query output stream (see \\o)\n");
fprintf(fout, " \\r -- reset (clear) the query buffer\n"); fprintf(fout, " \\r -- reset (clear) the query buffer\n");
fprintf(fout, " \\s [<fname>] -- print history or save it in <fname>\n"); fprintf(fout, " \\s [<fname>] -- print history or save it in <fname>\n");
fprintf(fout, " \\set <var> [<value>] -- set/unset internal variable\n"); fprintf(fout, " \\set <var> [<value>] -- set/unset internal variable\n");
fprintf(fout, " \\t -- don't show table headers or footers (currently %s)\n", ON(pset->popt.topt.tuples_only)); fprintf(fout, " \\t -- don't show table headers or footers (currently %s)\n", ON(pset->popt.topt.tuples_only));
fprintf(fout, " \\x -- toggle expanded output (currently %s)\n", ON(pset->popt.topt.expanded)); fprintf(fout, " \\x -- toggle expanded output (currently %s)\n", ON(pset->popt.topt.expanded));
fprintf(fout, " \\w <fname> -- write current query buffer to a file\n"); fprintf(fout, " \\w <fname> -- write current query buffer to a file\n");
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); {
pqsignal(SIGPIPE, SIG_DFL); pclose(fout);
} pqsignal(SIGPIPE, SIG_DFL);
}
} }
@ -212,59 +224,59 @@ 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 */
int i; /* Index into QL_HELP[] */
puts("Syntax: \\h <cmd> or \\help <cmd>, where <cmd> is one of the following:");
left_center_right = 'L';/* Start with left column */
i = 0;
while (QL_HELP[i].cmd != NULL)
{ {
switch (left_center_right) char left_center_right; /* Which column we're displaying */
{ int i; /* Index into QL_HELP[] */
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");
puts("Or type \\h * for a complete description of all commands.");
}
puts("Syntax: \\h <cmd> or \\help <cmd>, where <cmd> is one of the following:");
else left_center_right = 'L';/* Start with left column */
{ i = 0;
int i; while (QL_HELP[i].cmd != NULL)
bool help_found = false; {
switch (left_center_right)
for (i = 0; QL_HELP[i].cmd; i++) {
{ case 'L':
if (strcasecmp(QL_HELP[i].cmd, topic) == 0 || printf(" %-25s", QL_HELP[i].cmd);
strcmp(topic, "*") == 0) left_center_right = 'C';
{ break;
help_found = true; case 'C':
printf("Command: %s\nDescription: %s\nSyntax:\n%s\n\n", printf("%-25s", QL_HELP[i].cmd);
QL_HELP[i].cmd, QL_HELP[i].help, QL_HELP[i].syntax); 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");
puts("Or type \\h * for a complete description of all commands.");
} }
if (!help_found)
printf("No help available for '%s'.\nTry \\h with no arguments to see available help.\n", topic); else
} {
int i;
bool help_found = false;
for (i = 0; QL_HELP[i].cmd; i++)
{
if (strcasecmp(QL_HELP[i].cmd, topic) == 0 ||
strcmp(topic, "*") == 0)
{
help_found = true;
printf("Command: %s\nDescription: %s\nSyntax:\n%s\n\n",
QL_HELP[i].cmd, QL_HELP[i].help, QL_HELP[i].syntax);
}
}
if (!help_found)
printf("No help available for '%s'.\nTry \\h with no arguments to see available help.\n", topic);
}
} }
@ -273,34 +285,34 @@ helpSQL(const char *topic)
void 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) "
); );
} }

View File

@ -3,14 +3,13 @@
#include "settings.h" #include "settings.h"
void usage(void); void usage(void);
void slashUsage(PsqlSettings *pset); void slashUsage(PsqlSettings *pset);
void helpSQL(const char *topic); void helpSQL(const char *topic);
void print_copyright(void); void print_copyright(void);
#endif #endif

View File

@ -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,29 +27,31 @@ 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); {
fputc('\r', stdout); s = readline(prompt);
fflush(stdout); fputc('\r', stdout);
} fflush(stdout);
else { }
else
{
#endif #endif
fputs(prompt, stdout); fputs(prompt, stdout);
fflush(stdout); fflush(stdout);
s = gets_fromFile(stdin); s = gets_fromFile(stdin);
#ifdef USE_READLINE #ifdef USE_READLINE
} }
#endif #endif
#ifdef USE_HISTORY #ifdef USE_HISTORY
if (useHistory && s && s[0] != '\0') if (useHistory && s && s[0] != '\0')
add_history(s); add_history(s);
#endif #endif
return s; return s;
} }
@ -60,25 +64,27 @@ gets_interactive(const char *prompt)
char * char *
gets_fromFile(FILE *source) gets_fromFile(FILE *source)
{ {
PQExpBufferData buffer; PQExpBufferData buffer;
char line[1024]; char line[1024];
initPQExpBuffer(&buffer); initPQExpBuffer(&buffer);
while (fgets(line, 1024, source) != NULL) { while (fgets(line, 1024, source) != NULL)
appendPQExpBufferStr(&buffer, line); {
if (buffer.data[buffer.len-1] == '\n') { appendPQExpBufferStr(&buffer, line);
buffer.data[buffer.len-1] = '\0'; if (buffer.data[buffer.len - 1] == '\n')
return buffer.data; {
buffer.data[buffer.len - 1] = '\0';
return buffer.data;
}
} }
}
if (buffer.len > 0) if (buffer.len > 0)
return buffer.data; /* EOF after reading some bufferload(s) */ return buffer.data; /* EOF after reading some bufferload(s) */
/* EOF, so return null */ /* EOF, so return null */
termPQExpBuffer(&buffer); termPQExpBuffer(&buffer);
return NULL; return NULL;
} }
@ -93,28 +99,33 @@ void
initializeInput(int flags) initializeInput(int flags)
{ {
#ifdef USE_READLINE #ifdef USE_READLINE
if (flags == 1) { if (flags == 1)
useReadline = true; {
rl_readline_name = "psql"; useReadline = true;
} 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);
sprintf(psql_history, "%s/.psql_history", home);
read_history(psql_history); if (psql_history)
free(psql_history); {
} sprintf(psql_history, "%s/.psql_history", home);
read_history(psql_history);
free(psql_history);
}
}
} }
}
#endif #endif
} }
@ -124,17 +135,19 @@ bool
saveHistory(const char *fname) saveHistory(const char *fname)
{ {
#ifdef USE_HISTORY #ifdef USE_HISTORY
if (useHistory) { if (useHistory)
if (write_history(fname) != 0) { {
perror(fname); if (write_history(fname) != 0)
return false; {
perror(fname);
return false;
}
return true;
} }
return true; else
} return false;
else
return false;
#else #else
return false; return false;
#endif #endif
} }
@ -144,19 +157,22 @@ 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); {
if (psql_history) { psql_history = (char *) malloc(strlen(home) + 20);
sprintf(psql_history, "%s/.psql_history", home); if (psql_history)
write_history(psql_history); {
free(psql_history); sprintf(psql_history, "%s/.psql_history", home);
} write_history(psql_history);
free(psql_history);
}
}
} }
}
#endif #endif
} }

View File

@ -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

View File

@ -24,47 +24,50 @@
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; {
strncpy(notice, message, 79); (void) arg;
notice[79] = '\0'; strncpy(notice, message, 79);
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;
commit = (var && strcmp(var, "commit") == 0);
notice[0] = '\0';
old_notice_hook = PQsetNoticeProcessor(pset->db, _my_notice_handler, NULL);
res = PSQLexec(pset, commit ? "COMMIT" : "ROLLBACK");
if (!res)
return false;
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))
fputs(notice, stderr);
}
else if (!GetVariableBool(pset->vars, "quiet"))
{
if (commit)
puts("Warning: Your transaction in progress has been committed.");
else
puts("Warning: Your transaction in progress has been rolled back.");
}
PQsetNoticeProcessor(pset->db, old_notice_hook, NULL);
return true; return true;
commit = (var && strcmp(var, "commit")==0);
notice[0] = '\0';
old_notice_hook = PQsetNoticeProcessor(pset->db, _my_notice_handler, NULL);
res = PSQLexec(pset, commit ? "COMMIT" : "ROLLBACK");
if (!res)
return false;
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) )
fputs(notice, stderr);
}
else if (!GetVariableBool(pset->vars, "quiet")) {
if (commit)
puts("Warning: Your transaction in progress has been committed.");
else
puts("Warning: Your transaction in progress has been rolled back.");
}
PQsetNoticeProcessor(pset->db, old_notice_hook, NULL);
return true;
} }
@ -75,54 +78,61 @@ 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); {
return false; fputs("You are not connected to a database.\n", stderr);
} return false;
if (own_transaction) {
if (!handle_transaction(pset))
return false;
if (!(res = PSQLexec(pset, "BEGIN")))
return false;
PQclear(res);
}
status = lo_export(pset->db, atol(loid_arg), (char *)filename_arg);
if (status != 1) { /* of course this status is documented nowhere :( */
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
if (own_transaction) {
if (!(res = PSQLexec(pset, "COMMIT"))) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
} }
PQclear(res); if (own_transaction)
} {
if (!handle_transaction(pset))
return false;
fprintf(pset->queryFout, "lo_export\n"); if (!(res = PSQLexec(pset, "BEGIN")))
return false;
return true; PQclear(res);
}
status = lo_export(pset->db, atol(loid_arg), (char *) filename_arg);
if (status != 1)
{ /* of course this status is documented
* nowhere :( */
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
if (own_transaction)
{
if (!(res = PSQLexec(pset, "COMMIT")))
{
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
}
PQclear(res);
}
fprintf(pset->queryFout, "lo_export\n");
return true;
} }
@ -133,76 +143,85 @@ 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); {
return false; fputs("You are not connected to a database.\n", stderr);
} return false;
if (own_transaction) {
if (!handle_transaction(pset))
return false;
if (!(res = PSQLexec(pset, "BEGIN")))
return false;
PQclear(res);
}
loid = lo_import(pset->db, (char *)filename_arg);
if (loid == InvalidOid) {
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
} }
return false;
}
/* insert description if given */ if (own_transaction)
if (comment_arg) { {
sprintf(buf, "INSERT INTO pg_description VALUES (%d, '", loid); if (!handle_transaction(pset))
for (i=0; i<strlen(comment_arg); i++) return false;
if (comment_arg[i]=='\'')
strcat(buf, "\\'"); if (!(res = PSQLexec(pset, "BEGIN")))
else return false;
strncat(buf, &comment_arg[i], 1);
strcat(buf, "')");
if (!(res = PSQLexec(pset, buf))) {
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res); PQclear(res);
}
return false;
}
}
if (own_transaction) {
if (!(res = PSQLexec(pset, "COMMIT"))) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
} }
PQclear(res); loid = lo_import(pset->db, (char *) filename_arg);
} if (loid == InvalidOid)
{
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
/* insert description if given */
if (comment_arg)
{
sprintf(buf, "INSERT INTO pg_description VALUES (%d, '", loid);
for (i = 0; i < strlen(comment_arg); i++)
if (comment_arg[i] == '\'')
strcat(buf, "\\'");
else
strncat(buf, &comment_arg[i], 1);
strcat(buf, "')");
if (!(res = PSQLexec(pset, buf)))
{
if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
}
if (own_transaction)
{
if (!(res = PSQLexec(pset, "COMMIT")))
{
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
}
PQclear(res);
}
fprintf(pset->queryFout, "lo_import %d\n", loid); fprintf(pset->queryFout, "lo_import %d\n", loid);
return true; return true;
} }
@ -212,67 +231,76 @@ 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); {
return false; fputs("You are not connected to a database.\n", stderr);
} return false;
if (own_transaction) {
if (!handle_transaction(pset))
return false;
if (!(res = PSQLexec(pset, "BEGIN")))
return false;
PQclear(res);
}
status = lo_unlink(pset->db, loid);
if (status == -1) {
fputs(PQerrorMessage(pset->db), stderr);
if (own_transaction) {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
} }
return false;
}
/* remove the comment as well */ if (own_transaction)
sprintf(buf, "DELETE FROM pg_description WHERE objoid = %d", loid); {
if (!(res = PSQLexec(pset, buf))) { if (!handle_transaction(pset))
if (own_transaction) { return false;
res = PQexec(pset->db, "ROLLBACK");
PQclear(res); if (!(res = PSQLexec(pset, "BEGIN")))
return false;
PQclear(res);
} }
return false;
}
status = lo_unlink(pset->db, loid);
if (own_transaction) { if (status == -1)
if (!(res = PSQLexec(pset, "COMMIT"))) { {
res = PQexec(pset->db, "ROLLBACK"); fputs(PQerrorMessage(pset->db), stderr);
PQclear(res); if (own_transaction)
return false; {
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
}
/* remove the comment as well */
sprintf(buf, "DELETE FROM pg_description WHERE objoid = %d", loid);
if (!(res = PSQLexec(pset, buf)))
{
if (own_transaction)
{
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
}
return false;
} }
PQclear(res);
}
fprintf(pset->queryFout, "lo_unlink %d\n", loid); if (own_transaction)
{
if (!(res = PSQLexec(pset, "COMMIT")))
{
res = PQexec(pset->db, "ROLLBACK");
PQclear(res);
return false;
}
PQclear(res);
}
return true;
fprintf(pset->queryFout, "lo_unlink %d\n", loid);
return true;
} }
@ -282,30 +310,31 @@ 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;
descbuf[0] = '\0'; descbuf[0] = '\0';
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\"");
res = PSQLexec(pset, descbuf); res = PSQLexec(pset, descbuf);
if (!res) if (!res)
return false; return false;
myopt.topt.tuples_only = false; myopt.topt.tuples_only = false;
myopt.nullPrint = NULL; myopt.nullPrint = NULL;
myopt.title = "Large objects"; myopt.title = "Large objects";
printQuery(res, &myopt, pset->queryFout); printQuery(res, &myopt, pset->queryFout);
PQclear(res); PQclear(res);
return true; return true;
} }

View File

@ -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 */

View File

@ -26,343 +26,380 @@
int int
MainLoop(PsqlSettings *pset, FILE *source) MainLoop(PsqlSettings *pset, FILE *source)
{ {
PQExpBuffer query_buf; /* buffer for query being accumulated */ PQExpBuffer query_buf; /* buffer for query being accumulated */
char *line; /* current line of input */ char *line; /* current line of input */
char *xcomment; /* start of extended comment */ char *xcomment; /* start of extended comment */
int len; /* length of the line */ int len; /* length of the line */
int successResult = EXIT_SUCCESS; int successResult = EXIT_SUCCESS;
backslashResult slashCmdStatus; backslashResult slashCmdStatus;
bool eof = false; /* end of our command input? */ bool eof = false; /* end of our command input? */
bool success; bool success;
char in_quote; /* == 0 for no in_quote */ char in_quote; /* == 0 for no in_quote */
bool was_bslash; /* backslash */ bool was_bslash; /* backslash */
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;
bool prev_cmd_interactive; bool prev_cmd_interactive;
bool die_on_error; bool die_on_error;
const char *interpol_char; const char *interpol_char;
/* Save old settings */ /* Save old settings */
prev_cmd_source = pset->cur_cmd_source; prev_cmd_source = pset->cur_cmd_source;
prev_cmd_interactive = pset->cur_cmd_interactive; prev_cmd_interactive = pset->cur_cmd_interactive;
/* Establish new source */ /* Establish new source */
pset->cur_cmd_source = source; pset->cur_cmd_source = source;
pset->cur_cmd_interactive = ((source == stdin) && !pset->notty); pset->cur_cmd_interactive = ((source == stdin) && !pset->notty);
query_buf = createPQExpBuffer(); query_buf = createPQExpBuffer();
if (!query_buf) { if (!query_buf)
perror("createPQExpBuffer");
exit(EXIT_FAILURE);
}
xcomment = NULL;
in_quote = 0;
paren_level = 0;
slashCmdStatus = CMD_UNKNOWN; /* set default */
/* main loop to get queries and execute them */
while (!eof)
{
if (slashCmdStatus == CMD_NEWEDIT)
{ {
/* perror("createPQExpBuffer");
* just returned from editing the line? then just copy to the exit(EXIT_FAILURE);
* input buffer
*/
line = strdup(query_buf->data);
resetPQExpBuffer(query_buf);
/* reset parsing state since we are rescanning whole query */
xcomment = NULL;
in_quote = 0;
paren_level = 0;
} }
else
{
/*
* otherwise, set interactive prompt if necessary
* and get another line
*/
if (pset->cur_cmd_interactive)
{
int prompt_status;
if (in_quote && in_quote == '\'') xcomment = NULL;
prompt_status = PROMPT_SINGLEQUOTE; in_quote = 0;
else if (in_quote && in_quote == '"') paren_level = 0;
prompt_status= PROMPT_DOUBLEQUOTE; slashCmdStatus = CMD_UNKNOWN; /* set default */
else if (xcomment != NULL)
prompt_status = PROMPT_COMMENT;
else if (query_buf->len > 0) /* main loop to get queries and execute them */
prompt_status = PROMPT_CONTINUE; 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 = 0;
paren_level = 0;
}
else else
prompt_status = PROMPT_READY; {
line = gets_interactive(get_prompt(pset, prompt_status)); /*
} * otherwise, set interactive prompt if necessary and get
else * another line
line = gets_fromFile(source); */
} if (pset->cur_cmd_interactive)
{
int prompt_status;
if (in_quote && in_quote == '\'')
prompt_status = PROMPT_SINGLEQUOTE;
else if (in_quote && in_quote == '"')
prompt_status = PROMPT_DOUBLEQUOTE;
else if (xcomment != NULL)
prompt_status = PROMPT_COMMENT;
else if (query_buf->len > 0)
prompt_status = PROMPT_CONTINUE;
else
prompt_status = PROMPT_READY;
/* Setting these will not have effect until next line */ line = gets_interactive(get_prompt(pset, prompt_status));
die_on_error = GetVariableBool(pset->vars, "die_on_error"); }
interpol_char = GetVariable(pset->vars, "sql_interpol");; else
line = gets_fromFile(source);
/*
* 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.
*/
/* No more input. Time to quit, or \i done */
if (line == NULL || (!pset->cur_cmd_interactive && *line == '\0'))
{
if (GetVariableBool(pset->vars, "echo") && !GetVariableBool(pset->vars, "quiet"))
puts("EOF");
eof = true;
continue;
}
/* not currently inside an extended comment? */
if (xcomment)
xcomment = line;
/* strip trailing backslashes, they don't have a clear meaning */
while (1) {
char * cp = strrchr(line, '\\');
if (cp && (*(cp + 1) == '\0'))
*cp = '\0';
else
break;
}
/* echo back if input is from file and flag is set */
if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
fprintf(stderr, "%s\n", line);
/* interpolate variables into SQL */
len = strlen(line);
thislen = PQmblen(line);
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;
const char * value;
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];
line[i + thislen + in_length] = '\0';
value = interpolate_var(&line[i + thislen], pset);
out_length = strlen(value);
new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1);
if (!new) {
perror("malloc");
exit(EXIT_FAILURE);
} }
new[0] = '\0';
strncat(new, line, i);
strcat(new, value);
if (closer)
strcat(new, line + i + 2 + in_length);
free(line); /* Setting these will not have effect until next line */
line = new; die_on_error = GetVariableBool(pset->vars, "die_on_error");
i += out_length; interpol_char = GetVariable(pset->vars, "sql_interpol");;
}
}
/* nothing left on line? then ignore */
if (line[0] == '\0') {
free(line);
continue;
}
slashCmdStatus = CMD_UNKNOWN; /*
* 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.
*/
len = strlen(line); /* No more input. Time to quit, or \i done */
query_start = 0; if (line == NULL || (!pset->cur_cmd_interactive && *line == '\0'))
{
if (GetVariableBool(pset->vars, "echo") && !GetVariableBool(pset->vars, "quiet"))
puts("EOF");
eof = true;
continue;
}
/* /* not currently inside an extended comment? */
* Parse line, looking for command separators. if (xcomment)
* xcomment = line;
* The current character is at line[i], the prior character at
* line[i - prevlen], the next character at line[i + thislen].
*/ /* strip trailing backslashes, they don't have a clear meaning */
prevlen = 0; while (1)
thislen = (len > 0) ? PQmblen(line) : 0; {
char *cp = strrchr(line, '\\');
if (cp && (*(cp + 1) == '\0'))
*cp = '\0';
else
break;
}
/* echo back if input is from file and flag is set */
if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
fprintf(stderr, "%s\n", line);
/* interpolate variables into SQL */
len = strlen(line);
thislen = PQmblen(line);
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;
const char *value;
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];
line[i + thislen + in_length] = '\0';
value = interpolate_var(&line[i + thislen], pset);
out_length = strlen(value);
new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1);
if (!new)
{
perror("malloc");
exit(EXIT_FAILURE);
}
new[0] = '\0';
strncat(new, line, i);
strcat(new, value);
if (closer)
strcat(new, line + i + 2 + in_length);
free(line);
line = new;
i += out_length;
}
}
/* nothing left on line? then ignore */
if (line[0] == '\0')
{
free(line);
continue;
}
slashCmdStatus = CMD_UNKNOWN;
len = strlen(line);
query_start = 0;
/*
* 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].
*/
prevlen = 0;
thislen = (len > 0) ? PQmblen(line) : 0;
#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) {
break; if (!success && die_on_error)
break;
/* was the previous character a backslash? */ /* was the previous character a backslash? */
if (i > 0 && line[i - prevlen] == '\\') if (i > 0 && line[i - prevlen] == '\\')
was_bslash = true; was_bslash = true;
else else
was_bslash = false; was_bslash = false;
/* in quote? */ /* in quote? */
if (in_quote) { if (in_quote)
/* end of quote */ {
if (line[i] == in_quote && !was_bslash) /* end of quote */
in_quote = '\0'; if (line[i] == in_quote && !was_bslash)
} in_quote = '\0';
}
/* start of quote */ /* start of quote */
else if (line[i] == '\'' || line[i] == '"') else if (line[i] == '\'' || line[i] == '"')
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] == '/') { {
xcomment = NULL; if (line[i] == '*' && line[i + thislen] == '/')
ADVANCE_1; {
} xcomment = NULL;
} 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]; {
ADVANCE_1; xcomment = &line[i];
} ADVANCE_1;
}
/* single-line comment? truncate line */ /* single-line comment? truncate line */
else if ((line[i] == '-' && line[i + thislen] == '-') || else if ((line[i] == '-' && line[i + thislen] == '-') ||
(line[i] == '/' && line[i + thislen] == '/')) (line[i] == '/' && line[i + thislen] == '/'))
{ {
line[i] = '\0'; /* remove comment */ line[i] = '\0'; /* remove comment */
break; break;
} }
/* count nested parentheses */ /* count nested parentheses */
else if (line[i] == '(') else if (line[i] == '(')
paren_level++; paren_level++;
else if (line[i] == ')' && paren_level > 0) else if (line[i] == ')' && paren_level > 0)
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'; {
/* is there anything else on the line? */ line[i] = '\0';
if (line[query_start + strspn(line + query_start, " \t")]!='\0') { /* is there anything else on the line? */
/* insert a cosmetic newline, if this is not the first line in the buffer */ if (line[query_start + strspn(line + query_start, " \t")] != '\0')
if (query_buf->len > 0) {
appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */ /*
appendPQExpBufferStr(query_buf, line + query_start); * insert a cosmetic newline, if this is not the first
* line in the buffer
*/
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */
appendPQExpBufferStr(query_buf, line + query_start);
}
/* execute query */
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
query_start = i + thislen;
}
/* backslash command */
else if (was_bslash)
{
const char *end_of_cmd = NULL;
line[i - prevlen] = '\0'; /* overwrites backslash */
/* is there anything else on the line? */
if (line[query_start + strspn(line + query_start, " \t")] != '\0')
{
/*
* insert a cosmetic newline, if this is not the first
* line in the buffer
*/
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */
appendPQExpBufferStr(query_buf, line + query_start);
}
/* handle backslash command */
slashCmdStatus = HandleSlashCmds(pset, &line[i], query_buf, &end_of_cmd);
success = slashCmdStatus != CMD_ERROR;
if (slashCmdStatus == CMD_SEND)
{
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
query_start = i + thislen;
}
/* is there anything left after the backslash command? */
if (end_of_cmd)
{
i += end_of_cmd - &line[i];
query_start = i;
}
else
break;
}
} }
/* execute query */
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf); if (!success && die_on_error && !pset->cur_cmd_interactive)
query_start = i + thislen; {
} successResult = EXIT_USER;
break;
/* backslash command */
else if (was_bslash) {
const char * end_of_cmd = NULL;
line[i - prevlen] = '\0'; /* overwrites backslash */
/* is there anything else on the line? */
if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
/* insert a cosmetic newline, if this is not the first line in the buffer */
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
/* append the line to the query buffer */
appendPQExpBufferStr(query_buf, line + query_start);
} }
/* handle backslash command */
slashCmdStatus = HandleSlashCmds(pset, &line[i], query_buf, &end_of_cmd); if (slashCmdStatus == CMD_TERMINATE)
{
success = slashCmdStatus != CMD_ERROR; successResult = EXIT_SUCCESS;
break;
if (slashCmdStatus == CMD_SEND) {
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
query_start = i + thislen;
} }
/* is there anything left after the backslash command? */
if (end_of_cmd) { /* Put the rest of the line in the query buffer. */
i += end_of_cmd - &line[i]; if (line[query_start + strspn(line + query_start, " \t")] != '\0')
query_start = i; {
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
appendPQExpBufferStr(query_buf, line + query_start);
} }
else
break; free(line);
}
}
if (!success && die_on_error && !pset->cur_cmd_interactive) { /* In single line mode, send off the query if any */
successResult = EXIT_USER; if (query_buf->data[0] != '\0' && GetVariableBool(pset->vars, "singleline"))
break; {
} success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
}
if (slashCmdStatus == CMD_TERMINATE) { /* Have we lost the db connection? */
successResult = EXIT_SUCCESS; if (pset->db == NULL && !pset->cur_cmd_interactive)
break; {
} successResult = EXIT_BADCONN;
break;
}
} /* while */
destroyPQExpBuffer(query_buf);
/* Put the rest of the line in the query buffer. */ pset->cur_cmd_source = prev_cmd_source;
if (line[query_start + strspn(line + query_start, " \t")]!='\0') { pset->cur_cmd_interactive = prev_cmd_interactive;
if (query_buf->len > 0)
appendPQExpBufferChar(query_buf, '\n');
appendPQExpBufferStr(query_buf, line + query_start);
}
free(line); return successResult;
/* In single line mode, send off the query if any */
if (query_buf->data[0] != '\0' && GetVariableBool(pset->vars, "singleline")) {
success = SendQuery(pset, query_buf->data);
resetPQExpBuffer(query_buf);
}
/* Have we lost the db connection? */
if (pset->db == NULL && !pset->cur_cmd_interactive) {
successResult = EXIT_BADCONN;
break;
}
} /* while */
destroyPQExpBuffer(query_buf);
pset->cur_cmd_source = prev_cmd_source;
pset->cur_cmd_interactive = prev_cmd_interactive;
return successResult;
} /* MainLoop() */ } /* MainLoop() */

View File

@ -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 */

File diff suppressed because it is too large Load Diff

View File

@ -7,52 +7,58 @@
#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_UNALIGNED, PRINT_NOTHING = 0, /* to make sure someone initializes this */
PRINT_ALIGNED, PRINT_UNALIGNED,
PRINT_HTML, PRINT_ALIGNED,
PRINT_LATEX PRINT_HTML,
/* add your favourite output format here ... */ PRINT_LATEX
/* add your favourite output format here ... */
}; };
typedef struct _printTableOpt { typedef struct _printTableOpt
enum printFormat format; /* one of the above */ {
bool expanded; /* expanded/vertical output (if supported by output format) */ enum printFormat format; /* one of the above */
bool pager; /* use pager for output (if to stdout and stdout is a tty) */ bool expanded; /* expanded/vertical output (if supported
bool tuples_only; /* don't output headers, row counts, etc. */ * by output format) */
unsigned short int border; /* Print a border around the table. 0=none, 1=dividing lines, 2=full */ bool pager; /* use pager for output (if to stdout and
char *fieldSep; /* field separator for unaligned text mode */ * stdout is a tty) */
char *tableAttr; /* attributes for HTML <table ...> */ bool tuples_only; /* don't output headers, row counts, etc. */
} printTableOpt; 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 *tableAttr; /* attributes for HTML <table ...> */
} printTableOpt;
/* /*
* Use this to print just any table in the supported formats. * Use this to print just any table in the supported formats.
* - title is just any string (NULL is fine) * - title is just any string (NULL is fine)
* - headers is the column headings (NULL ptr terminated). It must be given and * - headers is the column headings (NULL ptr terminated). It must be given and
* complete since the column count is generated from this. * complete since the column count is generated from this.
* - cells are the data cells to be printed. Now you know why the correct * - cells are the data cells to be printed. Now you know why the correct
* column count is important * column count is important
* - footers are lines to be printed below the table * - footers are lines to be printed below the table
* - 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 */ {
char * nullPrint; /* how to print null entities */ printTableOpt topt; /* the options above */
bool quote; /* quote all values as much as possible */ char *nullPrint; /* how to print null entities */
char * title; /* override title */ bool quote; /* quote all values as much as possible */
char ** footers; /* override footer (default is "(xx rows)") */ char *title; /* override title */
} printQueryOpt; char **footers; /* override footer (default is "(xx
* rows)") */
} printQueryOpt;
/* /*
* Use this to print query results * Use this to print query results
@ -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 */

View File

@ -34,20 +34,20 @@
* %~ - like %/ but "~" when database name equals user name * %~ - like %/ but "~" when database name equals user name
* %# - "#" if the username is postgres, ">" otherwise * %# - "#" if the username is postgres, ">" otherwise
* %R - in prompt1 normally =, or ^ if single line mode, * %R - in prompt1 normally =, or ^ if single line mode,
* or a ! if session is not connected to a database; * or a ! if session is not connected to a database;
* in prompt2 -, *, ', or "; * in prompt2 -, *, ', or ";
* in prompt3 nothing * in prompt3 nothing
* %? - the error code of the last query (not yet implemented) * %? - the error code of the last query (not yet implemented)
* %% - a percent sign * %% - a percent sign
* *
* %[0-9] - the character with the given decimal code * %[0-9] - the character with the given decimal code
* %0[0-7] - the character with the given octal code * %0[0-7] - the character with the given octal code
* %0x[0-9A-Fa-f] - the character with the given hexadecimal code * %0x[0-9A-Fa-f] - the character with the given hexadecimal code
* *
* %`command` - The result of executing command in /bin/sh with trailing * %`command` - The result of executing command in /bin/sh with trailing
* newline stripped. * newline stripped.
* %$name$ - The value of the psql/environment/magic varible 'name' * %$name$ - The value of the psql/environment/magic varible 'name'
* (same rules as for, e.g., \echo $foo) * (same rules as for, e.g., \echo $foo)
* (those will not be rescanned for more escape sequences!) * (those will not be rescanned for more escape sequences!)
* *
* *
@ -60,197 +60,214 @@ 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 "";
if (status == PROMPT_READY) if (status == PROMPT_READY)
prompt_string = GetVariable(pset->vars, "prompt1"); prompt_string = GetVariable(pset->vars, "prompt1");
else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT) else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT)
prompt_string = GetVariable(pset->vars, "prompt2"); prompt_string = GetVariable(pset->vars, "prompt2");
else if (status == PROMPT_COPY) else if (status == PROMPT_COPY)
prompt_string = GetVariable(pset->vars, "prompt3"); prompt_string = GetVariable(pset->vars, "prompt3");
else
prompt_string = "? ";
destination[0] = '\0';
for (p = prompt_string;
p && *p && strlen(destination)<MAX_PROMPT_SIZE;
p++)
{
MemSet(buf, 0, MAX_PROMPT_SIZE+1);
if (esc)
{
switch (*p)
{
case '%':
strcpy(buf, "%");
break;
/* Current database */
case '/':
if (pset->db)
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
break;
case '~': {
const char * var;
if (pset->db) {
if ( strcmp(PQdb(pset->db), PQuser(pset->db))==0 ||
( (var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset->db))==0) )
strcpy(buf, "~");
else
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
}
break;
}
/* DB server hostname (long/short) */
case 'M':
case 'm':
if (pset->db) {
if (PQhost(pset->db)) {
strncpy(buf, PQhost(pset->db), MAX_PROMPT_SIZE);
if (*p == 'm')
buf[strcspn(buf,".")] = '\0';
}
else
buf[0] = '.';
}
break;
/* DB server port number */
case '>':
if (pset->db) {
if (PQhost(pset->db))
strncpy(buf, PQport(pset->db), MAX_PROMPT_SIZE);
else
buf[0] = '.';
}
break;
/* DB server user name */
case 'n':
if (pset->db)
strncpy(buf, PQuser(pset->db), MAX_PROMPT_SIZE);
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
long int l;
char * end;
l = strtol(p, &end, 0);
sprintf(buf, "%c", (unsigned char)l);
p = end-1;
break;
}
case 'R':
switch(status) {
case PROMPT_READY:
if (!pset->db)
buf[0] = '!';
else if (!GetVariableBool(pset->vars, "singleline"))
buf[0] = '=';
else
buf[0] = '^';
break;
case PROMPT_CONTINUE:
buf[0] = '-';
break;
case PROMPT_SINGLEQUOTE:
buf[0] = '\'';
break;
case PROMPT_DOUBLEQUOTE:
buf[0] = '"';
break;
case PROMPT_COMMENT:
buf[0] = '*';
break;
default:
buf[0] = '\0';
break;
}
case '?':
/* not here yet */
break;
case '#':
{
if (pset->db && strcmp(PQuser(pset->db), "postgres")==0)
buf[0] = '#';
else
buf[0] = '>';
break;
}
/* execute command */
case '`':
{
FILE * fd = NULL;
char * file = strdup(p+1);
int cmdend;
cmdend = strcspn(file, "`");
file[cmdend] = '\0';
if (file)
fd = popen(file, "r");
if (fd) {
fgets(buf, MAX_PROMPT_SIZE-1, fd);
pclose(fd);
}
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\0';
free(file);
p += cmdend+1;
break;
}
/* interpolate variable */
case '$':
{
char *name;
const char *val;
int nameend;
name = strdup(p+1);
nameend = strcspn(name, "$");
name[nameend] = '\0';
val = interpolate_var(name, pset);
if (val)
strncpy(buf, val, MAX_PROMPT_SIZE);
free(name);
p += nameend+1;
break;
}
default:
buf[0] = *p;
buf[1] = '\0';
}
esc = false;
}
else if (*p == '%')
esc = true;
else else
prompt_string = "? ";
destination[0] = '\0';
for (p = prompt_string;
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
p++)
{ {
buf[0] = *p; MemSet(buf, 0, MAX_PROMPT_SIZE + 1);
buf[1] = '\0'; if (esc)
esc = false; {
switch (*p)
{
case '%':
strcpy(buf, "%");
break;
/* Current database */
case '/':
if (pset->db)
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
break;
case '~':
{
const char *var;
if (pset->db)
{
if (strcmp(PQdb(pset->db), PQuser(pset->db)) == 0 ||
((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset->db)) == 0))
strcpy(buf, "~");
else
strncpy(buf, PQdb(pset->db), MAX_PROMPT_SIZE);
}
break;
}
/* DB server hostname (long/short) */
case 'M':
case 'm':
if (pset->db)
{
if (PQhost(pset->db))
{
strncpy(buf, PQhost(pset->db), MAX_PROMPT_SIZE);
if (*p == 'm')
buf[strcspn(buf, ".")] = '\0';
}
else
buf[0] = '.';
}
break;
/* DB server port number */
case '>':
if (pset->db)
{
if (PQhost(pset->db))
strncpy(buf, PQport(pset->db), MAX_PROMPT_SIZE);
else
buf[0] = '.';
}
break;
/* DB server user name */
case 'n':
if (pset->db)
strncpy(buf, PQuser(pset->db), MAX_PROMPT_SIZE);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
long int l;
char *end;
l = strtol(p, &end, 0);
sprintf(buf, "%c", (unsigned char) l);
p = end - 1;
break;
}
case 'R':
switch (status)
{
case PROMPT_READY:
if (!pset->db)
buf[0] = '!';
else if (!GetVariableBool(pset->vars, "singleline"))
buf[0] = '=';
else
buf[0] = '^';
break;
case PROMPT_CONTINUE:
buf[0] = '-';
break;
case PROMPT_SINGLEQUOTE:
buf[0] = '\'';
break;
case PROMPT_DOUBLEQUOTE:
buf[0] = '"';
break;
case PROMPT_COMMENT:
buf[0] = '*';
break;
default:
buf[0] = '\0';
break;
}
case '?':
/* not here yet */
break;
case '#':
{
if (pset->db && strcmp(PQuser(pset->db), "postgres") == 0)
buf[0] = '#';
else
buf[0] = '>';
break;
}
/* execute command */
case '`':
{
FILE *fd = NULL;
char *file = strdup(p + 1);
int cmdend;
cmdend = strcspn(file, "`");
file[cmdend] = '\0';
if (file)
fd = popen(file, "r");
if (fd)
{
fgets(buf, MAX_PROMPT_SIZE - 1, fd);
pclose(fd);
}
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
free(file);
p += cmdend + 1;
break;
}
/* interpolate variable */
case '$':
{
char *name;
const char *val;
int nameend;
name = strdup(p + 1);
nameend = strcspn(name, "$");
name[nameend] = '\0';
val = interpolate_var(name, pset);
if (val)
strncpy(buf, val, MAX_PROMPT_SIZE);
free(name);
p += nameend + 1;
break;
}
default:
buf[0] = *p;
buf[1] = '\0';
}
esc = false;
}
else if (*p == '%')
esc = true;
else
{
buf[0] = *p;
buf[1] = '\0';
esc = false;
}
if (!esc)
strncat(destination, buf, MAX_PROMPT_SIZE - strlen(destination));
} }
if (!esc) { destination[MAX_PROMPT_SIZE] = '\0';
strncat(destination, buf, MAX_PROMPT_SIZE-strlen(destination)); return destination;
}
}
destination[MAX_PROMPT_SIZE] = '\0';
return destination;
} }

View File

@ -3,17 +3,18 @@
#include "settings.h" #include "settings.h"
typedef enum _promptStatus { typedef enum _promptStatus
PROMPT_READY, {
PROMPT_CONTINUE, PROMPT_READY,
PROMPT_COMMENT, PROMPT_CONTINUE,
PROMPT_SINGLEQUOTE, PROMPT_COMMENT,
PROMPT_DOUBLEQUOTE, PROMPT_SINGLEQUOTE,
PROMPT_COPY PROMPT_DOUBLEQUOTE,
} promptStatus_t; PROMPT_COPY
} 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 */

File diff suppressed because it is too large Load Diff

View File

@ -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"},
\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! */
}; };

View File

@ -20,24 +20,27 @@
typedef struct _psqlSettings typedef struct _psqlSettings
{ {
PGconn *db; /* connection to backend */ PGconn *db; /* connection to backend */
FILE *queryFout; /* where to send the query results */ FILE *queryFout; /* where to send the query results */
bool queryFoutPipe; /* queryFout is from a popen() */ bool queryFoutPipe; /* queryFout is from a popen() */
printQueryOpt popt; printQueryOpt popt;
VariableSpace vars; /* "shell variable" repository */ VariableSpace vars; /* "shell variable" repository */
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
bool useReadline; /* use libreadline routines */ * determined on startup) */
bool useHistory; bool useReadline; /* use libreadline routines */
bool getPassword; /* prompt the user for a username and bool useHistory;
password */ bool getPassword; /* prompt the user for a username and
FILE * cur_cmd_source; /* describe the status of the current main loop */ * password */
bool cur_cmd_interactive; FILE *cur_cmd_source; /* describe the status of the current main
* loop */
bool cur_cmd_interactive;
bool has_client_encoding; /* was PGCLIENTENCODING set on startup? */ bool has_client_encoding; /* was PGCLIENTENCODING set on
* startup? */
} PsqlSettings; } PsqlSettings;

View File

@ -7,251 +7,251 @@
struct _helpStruct struct _helpStruct
{ {
char *cmd; /* the command name */ char *cmd; /* the command name */
char *help; /* the help associated with it */ char *help; /* the help associated with it */
char *syntax; /* the syntax associated with it */ char *syntax; /* the syntax associated with it */
}; };
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 */

View File

@ -37,35 +37,38 @@
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_SINGLE_SLASH, {
ACT_LIST_DB, ACT_NOTHING = 0,
ACT_SHOW_VER, ACT_SINGLE_SLASH,
ACT_SINGLE_QUERY, ACT_LIST_DB,
ACT_FILE ACT_SHOW_VER,
ACT_SINGLE_QUERY,
ACT_FILE
}; };
struct adhoc_opts { struct adhoc_opts
char * dbname; {
char * host; char *dbname;
char * port; char *host;
char * username; char *port;
enum _actions action; char *username;
char * action_string; enum _actions action;
bool no_readline; char *action_string;
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);
@ -77,133 +80,140 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
PsqlSettings settings; PsqlSettings settings;
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);
settings.cur_cmd_source = stdin; settings.cur_cmd_source = stdin;
settings.cur_cmd_interactive = false; settings.cur_cmd_interactive = false;
settings.vars = CreateVariableSpace(); settings.vars = CreateVariableSpace();
settings.popt.topt.format = PRINT_ALIGNED; settings.popt.topt.format = PRINT_ALIGNED;
settings.queryFout = stdout; settings.queryFout = stdout;
settings.popt.topt.fieldSep = strdup(DEFAULT_FIELD_SEP); settings.popt.topt.fieldSep = strdup(DEFAULT_FIELD_SEP);
settings.popt.topt.border = 1; settings.popt.topt.border = 1;
SetVariable(settings.vars, "prompt1", DEFAULT_PROMPT1); SetVariable(settings.vars, "prompt1", DEFAULT_PROMPT1);
SetVariable(settings.vars, "prompt2", DEFAULT_PROMPT2); SetVariable(settings.vars, "prompt2", DEFAULT_PROMPT2);
SetVariable(settings.vars, "prompt3", DEFAULT_PROMPT3); SetVariable(settings.vars, "prompt3", DEFAULT_PROMPT3);
settings.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); settings.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
/* This is obsolete and will be removed very soon. */ /* This is obsolete and will be removed very soon. */
#ifdef PSQL_ALWAYS_GET_PASSWORDS #ifdef PSQL_ALWAYS_GET_PASSWORDS
settings.getPassword = true; settings.getPassword = true;
#else #else
settings.getPassword = false; settings.getPassword = false;
#endif #endif
#ifdef MULTIBYTE #ifdef MULTIBYTE
settings.has_client_encoding = (getenv("PGCLIENTENCODING") != NULL); settings.has_client_encoding = (getenv("PGCLIENTENCODING") != NULL);
#endif #endif
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) {
username = simple_prompt("Username: ", 100, true); if (strcmp(options.username, "?") == 0)
else username = simple_prompt("Username: ", 100, true);
username = strdup(options.username); else
} username = strdup(options.username);
if (settings.getPassword)
password = simple_prompt("Password: ", 100, false);
/* loop until we have a password if requested by backend */
do {
need_pass = false;
settings.db = PQsetdbLogin(options.host, options.port, NULL, NULL, options.dbname, username, password);
if (PQstatus(settings.db)==CONNECTION_BAD &&
strcmp(PQerrorMessage(settings.db), "fe_sendauth: no password supplied\n")==0) {
need_pass = true;
free(password);
password = NULL;
password = simple_prompt("Password: ", 100, false);
} }
} while (need_pass);
free(username); if (settings.getPassword)
free(password); password = simple_prompt("Password: ", 100, false);
if (PQstatus(settings.db) == CONNECTION_BAD) { /* loop until we have a password if requested by backend */
fprintf(stderr, "Connection to database '%s' failed.\n%s\n", PQdb(settings.db), PQerrorMessage(settings.db)); do
{
need_pass = false;
settings.db = PQsetdbLogin(options.host, options.port, NULL, NULL, options.dbname, username, password);
if (PQstatus(settings.db) == CONNECTION_BAD &&
strcmp(PQerrorMessage(settings.db), "fe_sendauth: no password supplied\n") == 0)
{
need_pass = true;
free(password);
password = NULL;
password = simple_prompt("Password: ", 100, false);
}
} while (need_pass);
free(username);
free(password);
if (PQstatus(settings.db) == CONNECTION_BAD)
{
fprintf(stderr, "Connection to database '%s' failed.\n%s\n", PQdb(settings.db), PQerrorMessage(settings.db));
PQfinish(settings.db);
exit(EXIT_BADCONN);
}
if (options.action == ACT_LIST_DB)
{
int success = listAllDbs(&settings);
PQfinish(settings.db);
exit(!success);
}
if (options.action == ACT_SHOW_VER)
{
showVersion(&settings, true);
PQfinish(settings.db);
exit(EXIT_SUCCESS);
}
if (!GetVariable(settings.vars, "quiet") && !settings.notty && !options.action)
{
puts("Welcome to psql, the PostgreSQL interactive terminal.\n"
"(Please type \\copyright to see the distribution terms of PostgreSQL.)");
//showVersion(&settings, false);
puts("\n"
"Type \\h for help with SQL commands,\n"
" \\? for help on internal slash commands,\n"
" \\q to quit,\n"
" \\g or terminate with semicolon to execute query.");
}
process_psqlrc(&settings);
initializeInput(options.no_readline ? 0 : 1);
/* Now find something to do */
/* process file given by -f */
if (options.action == ACT_FILE)
successResult = process_file(options.action_string, &settings) ? 0 : 1;
/* process slash command if one was given to -c */
else if (options.action == ACT_SINGLE_SLASH)
successResult = HandleSlashCmds(&settings, options.action_string, NULL, NULL) != CMD_ERROR ? 0 : 1;
/* If the query given to -c was a normal one, send it */
else if (options.action == ACT_SINGLE_QUERY)
successResult = SendQuery(&settings, options.action_string) ? 0 : 1;
/* or otherwise enter interactive main loop */
else
successResult = MainLoop(&settings, stdin);
/* clean up */
finishInput();
PQfinish(settings.db); PQfinish(settings.db);
exit(EXIT_BADCONN); setQFout(NULL, &settings);
} DestroyVariableSpace(settings.vars);
if (options.action == ACT_LIST_DB) { return successResult;
int success = listAllDbs(&settings);
PQfinish(settings.db);
exit (!success);
}
if (options.action == ACT_SHOW_VER) {
showVersion(&settings, true);
PQfinish(settings.db);
exit (EXIT_SUCCESS);
}
if (!GetVariable(settings.vars, "quiet") && !settings.notty && !options.action)
{
puts("Welcome to psql, the PostgreSQL interactive terminal.\n"
"(Please type \\copyright to see the distribution terms of PostgreSQL.)");
// showVersion(&settings, false);
puts("\n"
"Type \\h for help with SQL commands,\n"
" \\? for help on internal slash commands,\n"
" \\q to quit,\n"
" \\g or terminate with semicolon to execute query.");
}
process_psqlrc(&settings);
initializeInput(options.no_readline ? 0 : 1);
/* Now find something to do */
/* process file given by -f */
if (options.action == ACT_FILE)
successResult = process_file(options.action_string, &settings) ? 0 : 1;
/* process slash command if one was given to -c */
else if (options.action == ACT_SINGLE_SLASH)
successResult = HandleSlashCmds(&settings, options.action_string, NULL, NULL) != CMD_ERROR ? 0 : 1;
/* If the query given to -c was a normal one, send it */
else if (options.action == ACT_SINGLE_QUERY)
successResult = SendQuery(&settings, options.action_string) ? 0 : 1;
/* or otherwise enter interactive main loop */
else
successResult = MainLoop(&settings, stdin);
/* clean up */
finishInput();
PQfinish(settings.db);
setQFout(NULL, &settings);
DestroyVariableSpace(settings.vars);
return successResult;
} }
@ -214,215 +224,231 @@ 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;
extern int optind; extern int optind;
int c; int c;
MemSet(options, 0, sizeof *options); MemSet(options, 0, sizeof *options);
#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. */
while ((c = getopt(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?-")) != -1) /*
* 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)
#endif #endif
{
switch (c)
{ {
case 'A': switch (c)
pset->popt.topt.format = PRINT_UNALIGNED; {
break; case 'A':
case 'c': pset->popt.topt.format = PRINT_UNALIGNED;
options->action_string = optarg; break;
if (optarg[0] == '\\') case 'c':
options->action = ACT_SINGLE_SLASH; options->action_string = optarg;
else if (optarg[0] == '\\')
options->action = ACT_SINGLE_QUERY; options->action = ACT_SINGLE_SLASH;
break; else
case 'd': options->action = ACT_SINGLE_QUERY;
options->dbname = optarg; break;
break; case 'd':
case 'e': options->dbname = optarg;
SetVariable(pset->vars, "echo", ""); break;
break; case 'e':
case 'E': SetVariable(pset->vars, "echo", "");
SetVariable(pset->vars, "echo_secret", ""); break;
break; case 'E':
case 'f': SetVariable(pset->vars, "echo_secret", "");
options->action = ACT_FILE; break;
options->action_string = optarg; case 'f':
break; options->action = ACT_FILE;
case 'F': options->action_string = optarg;
pset->popt.topt.fieldSep = strdup(optarg); break;
break; case 'F':
case 'h': pset->popt.topt.fieldSep = strdup(optarg);
options->host = optarg; break;
break; case 'h':
case 'H': options->host = optarg;
pset->popt.topt.format = PRINT_HTML; break;
break; case 'H':
case 'l': pset->popt.topt.format = PRINT_HTML;
options->action = ACT_LIST_DB; break;
break; case 'l':
case 'n': options->action = ACT_LIST_DB;
options->no_readline = true; break;
break; case 'n':
case 'o': options->no_readline = true;
setQFout(optarg, pset); break;
break; case 'o':
case 'p': setQFout(optarg, pset);
options->port = optarg; break;
break; case 'p':
case 'P': options->port = optarg;
{ break;
char *value; case 'P':
char *equal_loc; {
bool result; char *value;
char *equal_loc;
bool result;
value = xstrdup(optarg); value = xstrdup(optarg);
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'; {
result = do_pset(value, equal_loc+1, &pset->popt, true); *equal_loc = '\0';
} result = do_pset(value, equal_loc + 1, &pset->popt, true);
}
if (!result) { if (!result)
fprintf(stderr, "Couldn't set printing paramter %s.\n", value); {
exit(EXIT_FAILURE); fprintf(stderr, "Couldn't set printing paramter %s.\n", value);
} exit(EXIT_FAILURE);
}
free(value); free(value);
break; break;
} }
case 'q': case 'q':
SetVariable(pset->vars, "quiet", ""); SetVariable(pset->vars, "quiet", "");
break; break;
case 's': case 's':
SetVariable(pset->vars, "singlestep", ""); SetVariable(pset->vars, "singlestep", "");
break; break;
case 'S': case 'S':
SetVariable(pset->vars, "singleline", ""); SetVariable(pset->vars, "singleline", "");
break; break;
case 't': case 't':
pset->popt.topt.tuples_only = true; pset->popt.topt.tuples_only = true;
break; break;
case 'T': case 'T':
pset->popt.topt.tableAttr = xstrdup(optarg); pset->popt.topt.tableAttr = xstrdup(optarg);
break; break;
case 'u': case 'u':
pset->getPassword = true; pset->getPassword = true;
options->username = "?"; options->username = "?";
break; break;
case 'U': case 'U':
options->username = optarg; options->username = optarg;
break; break;
case 'x': case 'x':
pset->popt.topt.expanded = true; pset->popt.topt.expanded = true;
break; break;
case 'v': case 'v':
{ {
char *value; char *value;
char *equal_loc; char *equal_loc;
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)) { {
fprintf(stderr, "Couldn't delete variable %s.\n", value); if (!DeleteVariable(pset->vars, value))
exit(EXIT_FAILURE); {
} fprintf(stderr, "Couldn't delete variable %s.\n", value);
} exit(EXIT_FAILURE);
else { }
*equal_loc = '\0'; }
if (!SetVariable(pset->vars, value, equal_loc+1)) { else
fprintf(stderr, "Couldn't set variable %s to %s.\n", value, equal_loc); {
exit(EXIT_FAILURE); *equal_loc = '\0';
} if (!SetVariable(pset->vars, value, equal_loc + 1))
} {
fprintf(stderr, "Couldn't set variable %s to %s.\n", value, equal_loc);
exit(EXIT_FAILURE);
}
}
free(value); free(value);
break; break;
} }
case 'V': case 'V':
options->action = ACT_SHOW_VER; options->action = ACT_SHOW_VER;
break; break;
case 'W': case 'W':
pset->getPassword = true; pset->getPassword = true;
break; break;
case '?': case '?':
usage(); usage();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
break; break;
#ifndef HAVE_GETOPT_LONG #ifndef HAVE_GETOPT_LONG
case '-': case '-':
fprintf(stderr, "This version of psql was compiled without support for long options.\n" fprintf(stderr, "This version of psql was compiled without support for long options.\n"
"Use -? for help on invocation options.\n"); "Use -? for help on invocation options.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
break; break;
#endif #endif
default: default:
usage(); usage();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
break; break;
}
} }
}
/* 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
if (!options->dbname) * username
options->dbname = argv[optind]; */
else if (!options->username) while (argc - optind >= 1)
options->username = argv[optind]; {
else if (!options->dbname)
fprintf(stderr, "Warning: extra option %s ignored.\n", argv[optind]); options->dbname = argv[optind];
else if (!options->username)
options->username = argv[optind];
else
fprintf(stderr, "Warning: extra option %s ignored.\n", argv[optind]);
optind++; optind++;
} }
} }
@ -431,41 +457,44 @@ 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); {
if (!psqlrc) { psqlrc = (char *) malloc(strlen(home) + 20);
perror("malloc"); if (!psqlrc)
exit(EXIT_FAILURE); {
perror("malloc");
exit(EXIT_FAILURE);
}
sprintf(psqlrc, "%s/.psqlrc-" PG_RELEASE "." PG_VERSION "." PG_SUBVERSION, home);
if (access(psqlrc, R_OK) == 0)
process_file(psqlrc, pset);
else
{
sprintf(psqlrc, "%s/.psqlrc", home);
if (access(psqlrc, R_OK) == 0)
process_file(psqlrc, pset);
}
free(psqlrc);
} }
sprintf(psqlrc, "%s/.psqlrc-"PG_RELEASE"."PG_VERSION"."PG_SUBVERSION, home);
if (access(psqlrc, R_OK) == 0)
process_file(psqlrc, pset);
else {
sprintf(psqlrc, "%s/.psqlrc", home);
if (access(psqlrc, R_OK) == 0)
process_file(psqlrc, pset);
}
free(psqlrc);
}
} }
@ -482,62 +511,68 @@ process_psqlrc(PsqlSettings * pset)
static void static void
showVersion(PsqlSettings *pset, bool verbose) 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); {
PQclear(res); if (versionstr)
return; puts(versionstr);
} PQclear(res);
return;
}
if (strncmp(versionstr, "PostgreSQL ", 11) == 0) { if (strncmp(versionstr, "PostgreSQL ", 11) == 0)
char *tmp; {
release = strtol(&versionstr[11], &tmp, 10); char *tmp;
version = strtol(tmp+1, &tmp, 10);
subversion = strtol(tmp+1, &tmp, 10);
}
printf("Server: %s\npsql", versionstr ? versionstr : "(could not connected)"); release = strtol(&versionstr[11], &tmp, 10);
version = strtol(tmp + 1, &tmp, 10);
subversion = strtol(tmp + 1, &tmp, 10);
}
if (strcmp(versionstr, PG_VERSION_STR) != 0) printf("Server: %s\npsql", versionstr ? versionstr : "(could not connected)");
printf(&PG_VERSION_STR[strcspn(PG_VERSION_STR, " ")]);
printf(" ("__DATE__" "__TIME__")"); if (strcmp(versionstr, PG_VERSION_STR) != 0)
printf(&PG_VERSION_STR[strcspn(PG_VERSION_STR, " ")]);
printf(" (" __DATE__ " " __TIME__ ")");
#ifdef MULTIBYTE #ifdef MULTIBYTE
printf(", multibyte"); printf(", multibyte");
#endif #endif
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
printf(", long options"); printf(", long options");
#endif #endif
#ifdef USE_READLINE #ifdef USE_READLINE
printf(", readline"); printf(", readline");
#endif #endif
#ifdef USE_HISTORY #ifdef USE_HISTORY
printf(", history"); printf(", history");
#endif #endif
#ifdef USE_LOCALE #ifdef USE_LOCALE
printf(", locale"); printf(", locale");
#endif #endif
#ifdef PSQL_ALWAYS_GET_PASSWORDS #ifdef PSQL_ALWAYS_GET_PASSWORDS
printf(", always password"); printf(", always password");
#endif #endif
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
printf(", assert checks"); printf(", assert checks");
#endif #endif
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.");
PQclear(res); PQclear(res);
} }

View File

@ -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,118 +19,130 @@
static void static void
unescape_quotes(char *source, char quote, char escape); unescape_quotes(char *source, char quote, char escape);
/* /*
* Replacement for strtok() (a.k.a. poor man's flex) * Replacement for strtok() (a.k.a. poor man's flex)
* *
* The calling convention is similar to that of strtok. * The calling convention is similar to that of strtok.
* s - string to parse, if NULL continue parsing the last string * s - string to parse, if NULL continue parsing the last string
* delim - set of characters that delimit tokens (usually whitespace) * delim - set of characters that delimit tokens (usually whitespace)
* quote - set of characters that quote stuff, they're not part of the token * quote - set of characters that quote stuff, they're not part of the token
* escape - character than can quote quotes * escape - character than can quote quotes
* was_quoted - if not NULL, stores the quoting character if any was encountered * was_quoted - if not NULL, stores the quoting character if any was encountered
* token_pos - if not NULL, receives a count to the start of the token in the * token_pos - if not NULL, receives a count to the start of the token in the
* parsed string * parsed string
* *
* 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 *
const char *delim, strtokx(const char *s,
const char *quote, const char *delim,
char escape, const char *quote,
char * was_quoted, char escape,
unsigned int * token_pos) char *was_quoted,
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 */
/* variously abused variables: */ static char *string = NULL; /* pointer into storage where to continue
unsigned int offset; * on next call */
char * start;
char *cp = NULL;
if (s) { /* variously abused variables: */
free(storage); unsigned int offset;
storage = strdup(s); char *start;
string = storage; char *cp = NULL;
}
if (!storage) if (s)
return NULL; {
free(storage);
storage = strdup(s);
string = storage;
}
/* skip leading "whitespace" */ if (!storage)
offset = strspn(string, delim); return NULL;
/* end of string reached */ /* skip leading "whitespace" */
if (string[offset] == '\0') { offset = strspn(string, delim);
/* technically we don't need to free here, but we're nice */
free(storage);
storage = NULL;
string = NULL;
return NULL;
}
/* test if quoting character */ /* end of string reached */
if (quote) if (string[offset] == '\0')
cp = strchr(quote, string[offset]); {
/* technically we don't need to free here, but we're nice */
free(storage);
storage = NULL;
string = NULL;
return NULL;
}
if (cp) { /* test if quoting character */
/* okay, we have a quoting character, now scan for the closer */ if (quote)
char *p; cp = strchr(quote, string[offset]);
start = &string[offset+1];
if (cp)
{
/* okay, we have a quoting character, now scan for the closer */
char *p;
start = &string[offset + 1];
if (token_pos)
*token_pos = start - storage;
for (p = start;
*p && (*p != *cp || *(p - 1) == escape);
#ifdef MULTIBYTE
p += PQmblen(p)
#else
p++
#endif
);
/* not yet end of string? */
if (*p != '\0')
{
*p = '\0';
string = p + 1;
if (was_quoted)
*was_quoted = *cp;
unescape_quotes(start, *cp, escape);
return start;
}
else
{
if (was_quoted)
*was_quoted = *cp;
string = p;
unescape_quotes(start, *cp, escape);
return start;
}
}
/* otherwise no quoting character. scan till next delimiter */
start = &string[offset];
if (token_pos) if (token_pos)
*token_pos = start - storage; *token_pos = start - storage;
for(p = start; offset = strcspn(start, delim);
*p && (*p != *cp || *(p-1) == escape) ; if (was_quoted)
#ifdef MULTIBYTE *was_quoted = 0;
p += PQmblen(p)
#else
p++
#endif
);
/* not yet end of string? */ if (start[offset] != '\0')
if (*p != '\0') { {
*p = '\0'; start[offset] = '\0';
string = p + 1; string = &start[offset] + 1;
if (was_quoted)
*was_quoted = *cp; return start;
unescape_quotes (start, *cp, escape);
return start;
} }
else { else
if (was_quoted) {
*was_quoted = *cp; string = &start[offset];
string = p; return start;
unescape_quotes (start, *cp, escape);
return start;
} }
}
/* otherwise no quoting character. scan till next delimiter */
start = &string[offset];
if (token_pos)
*token_pos = start - storage;
offset = strcspn(start, delim);
if (was_quoted)
*was_quoted = 0;
if (start[offset] != '\0') {
start[offset] = '\0';
string = &start[offset]+1;
return start;
}
else {
string = &start[offset];
return start;
}
} }
@ -142,38 +156,41 @@ char * strtokx(const char *s,
static void 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"); {
exit(EXIT_FAILURE); perror("calloc");
} exit(EXIT_FAILURE);
tmp = destination;
for (p = source; *p; p++)
{
char c;
if (*p == escape && *(p+1) && quote == *(p+1)) {
c = *(p+1);
p++;
} }
else
c = *p;
*tmp = c; tmp = destination;
tmp ++;
}
/* Terminating null character */ for (p = source; *p; p++)
*tmp = '\0'; {
char c;
strcpy(source, destination); if (*p == escape && *(p + 1) && quote == *(p + 1))
{
c = *(p + 1);
p++;
}
else
c = *p;
*tmp = c;
tmp++;
}
/* Terminating null character */
*tmp = '\0';
strcpy(source, destination);
} }

View File

@ -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 */

View File

@ -6,127 +6,145 @@
#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->value); free(ptr->name);
free(ptr); free(ptr->value);
return NULL; free(ptr);
} return NULL;
return ptr;
}
const char * GetVariable(VariableSpace space, const char * name)
{
struct _variable *current;
if (!space)
return NULL;
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name)) return NULL;
for (current = space; current; current = current->next) {
#ifdef USE_ASSERT_CHECKING
assert(current->name);
assert(current->value);
#endif
if (strcmp(current->name, name)==0)
return current->value;
}
return NULL;
}
bool GetVariableBool(VariableSpace space, const char * name)
{
return GetVariable(space, name)!=NULL ? true : false;
}
bool SetVariable(VariableSpace space, const char * name, const char * value)
{
struct _variable *current, *previous;
if (!space)
return false;
if (!value)
return DeleteVariable(space, name);
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name)) return false;
for (current = space; current; previous = current, current = current->next) {
#ifdef USE_ASSERT_CHECKING
assert(current->name);
assert(current->value);
#endif
if (strcmp(current->name, name)==0) {
free (current->value);
current->value = strdup(value);
return current->value ? true : false;
} }
}
previous->next = calloc(1, sizeof *(previous->next)); return ptr;
if (!previous->next)
return false;
previous->next->name = strdup(name);
if (!previous->next->name)
return false;
previous->next->value = strdup(value);
return previous->next->value ? true : false;
} }
bool DeleteVariable(VariableSpace space, const char * name) const char *
GetVariable(VariableSpace space, const char *name)
{ {
struct _variable *current, *previous; struct _variable *current;
if (!space) if (!space)
return false; return NULL;
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name)) return false; if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
return NULL;
for (current = space, previous = NULL; current; previous = 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)
free (current->name); return current->value;
free (current->value);
if (previous)
previous->next = current->next;
free(current);
return true;
} }
}
return true; return NULL;
} }
void DestroyVariableSpace(VariableSpace space) bool
GetVariableBool(VariableSpace space, const char *name)
{ {
if (!space) return GetVariable(space, name) != NULL ? true : false;
return; }
DestroyVariableSpace(space->next);
free(space);
bool
SetVariable(VariableSpace space, const char *name, const char *value)
{
struct _variable *current,
*previous;
if (!space)
return false;
if (!value)
return DeleteVariable(space, name);
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
return false;
for (current = space; current; previous = current, current = current->next)
{
#ifdef USE_ASSERT_CHECKING
assert(current->name);
assert(current->value);
#endif
if (strcmp(current->name, name) == 0)
{
free(current->value);
current->value = strdup(value);
return current->value ? true : false;
}
}
previous->next = calloc(1, sizeof *(previous->next));
if (!previous->next)
return false;
previous->next->name = strdup(name);
if (!previous->next->name)
return false;
previous->next->value = strdup(value);
return previous->next->value ? true : false;
}
bool
DeleteVariable(VariableSpace space, const char *name)
{
struct _variable *current,
*previous;
if (!space)
return false;
if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name))
return false;
for (current = space, previous = NULL; current; previous = current, current = current->next)
{
#ifdef USE_ASSERT_CHECKING
assert(current->name);
assert(current->value);
#endif
if (strcmp(current->name, name) == 0)
{
free(current->name);
free(current->value);
if (previous)
previous->next = current->next;
free(current);
return true;
}
}
return true;
}
void
DestroyVariableSpace(VariableSpace space)
{
if (!space)
return;
DestroyVariableSpace(space->next);
free(space);
} }

View File

@ -13,21 +13,22 @@
#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);
#endif /* VARIABLES_H */ #endif /* VARIABLES_H */