psql cleanup
This commit is contained in:
parent
2323b63631
commit
0e6652e673
File diff suppressed because it is too large
Load Diff
|
@ -11,39 +11,36 @@
|
|||
|
||||
|
||||
|
||||
typedef enum _backslashResult {
|
||||
CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */
|
||||
CMD_SEND, /* query complete; send off */
|
||||
CMD_SKIP_LINE, /* keep building query */
|
||||
CMD_TERMINATE, /* quit program */
|
||||
CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */
|
||||
CMD_ERROR /* the execution of the backslash command resulted
|
||||
in an error */
|
||||
} backslashResult;
|
||||
typedef enum _backslashResult
|
||||
{
|
||||
CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */
|
||||
CMD_SEND, /* query complete; send off */
|
||||
CMD_SKIP_LINE, /* keep building query */
|
||||
CMD_TERMINATE, /* quit program */
|
||||
CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */
|
||||
CMD_ERROR /* the execution of the backslash command
|
||||
* resulted in an error */
|
||||
} backslashResult;
|
||||
|
||||
|
||||
|
||||
backslashResult
|
||||
HandleSlashCmds(PsqlSettings *pset,
|
||||
const char *line,
|
||||
PQExpBuffer query_buf,
|
||||
const char ** end_of_cmd);
|
||||
backslashResult HandleSlashCmds(PsqlSettings *pset,
|
||||
const char *line,
|
||||
PQExpBuffer query_buf,
|
||||
const char **end_of_cmd);
|
||||
|
||||
bool
|
||||
do_connect(const char *new_dbname,
|
||||
const char *new_user,
|
||||
PsqlSettings *pset);
|
||||
bool do_connect(const char *new_dbname,
|
||||
const char *new_user,
|
||||
PsqlSettings *pset);
|
||||
|
||||
bool
|
||||
process_file(const char *filename,
|
||||
PsqlSettings *pset);
|
||||
bool process_file(const char *filename,
|
||||
PsqlSettings *pset);
|
||||
|
||||
|
||||
bool
|
||||
do_pset(const char * param,
|
||||
const char * value,
|
||||
printQueryOpt * popt,
|
||||
bool quiet);
|
||||
bool do_pset(const char *param,
|
||||
const char *value,
|
||||
printQueryOpt * popt,
|
||||
bool quiet);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <signal.h>
|
||||
#include <assert.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h> /* for write() */
|
||||
#include <unistd.h> /* for write() */
|
||||
#endif
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
@ -39,19 +39,23 @@
|
|||
* "Safe" wrapper around strdup()
|
||||
* (Using this also avoids writing #ifdef HAVE_STRDUP in every file :)
|
||||
*/
|
||||
char * xstrdup(const char * string)
|
||||
char *
|
||||
xstrdup(const char *string)
|
||||
{
|
||||
char * tmp;
|
||||
if (!string) {
|
||||
fprintf(stderr, "xstrdup: Cannot duplicate null pointer.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
tmp = strdup(string);
|
||||
if (!tmp) {
|
||||
perror("strdup");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return tmp;
|
||||
char *tmp;
|
||||
|
||||
if (!string)
|
||||
{
|
||||
fprintf(stderr, "xstrdup: Cannot duplicate null pointer.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
tmp = strdup(string);
|
||||
if (!tmp)
|
||||
{
|
||||
perror("strdup");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
|
@ -67,66 +71,67 @@ char * xstrdup(const char * string)
|
|||
bool
|
||||
setQFout(const char *fname, PsqlSettings *pset)
|
||||
{
|
||||
bool status = true;
|
||||
bool status = true;
|
||||
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
assert(pset);
|
||||
assert(pset);
|
||||
#else
|
||||
if (!pset) return false;
|
||||
if (!pset)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
/* Close old file/pipe */
|
||||
if (pset->queryFout && pset->queryFout != stdout && pset->queryFout != stderr)
|
||||
{
|
||||
if (pset->queryFoutPipe)
|
||||
pclose(pset->queryFout);
|
||||
/* Close old file/pipe */
|
||||
if (pset->queryFout && pset->queryFout != stdout && pset->queryFout != stderr)
|
||||
{
|
||||
if (pset->queryFoutPipe)
|
||||
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
|
||||
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");
|
||||
pset->queryFout = fopen(fname, "w");
|
||||
#else
|
||||
pset->queryFout = popen(pipename, "wb");
|
||||
pset->queryFout = fopen(fname, "wb");
|
||||
#endif
|
||||
pset->queryFoutPipe = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef __CYGWIN32__
|
||||
pset->queryFout = fopen(fname, "w");
|
||||
#else
|
||||
pset->queryFout = fopen(fname, "wb");
|
||||
#endif
|
||||
pset->queryFoutPipe = false;
|
||||
}
|
||||
pset->queryFoutPipe = false;
|
||||
}
|
||||
|
||||
if (!pset->queryFout)
|
||||
{
|
||||
perror(fname);
|
||||
pset->queryFout = stdout;
|
||||
pset->queryFoutPipe = false;
|
||||
status = false;
|
||||
}
|
||||
if (!pset->queryFout)
|
||||
{
|
||||
perror(fname);
|
||||
pset->queryFout = stdout;
|
||||
pset->queryFoutPipe = false;
|
||||
status = false;
|
||||
}
|
||||
|
||||
/* Direct signals */
|
||||
if (pset->queryFoutPipe)
|
||||
pqsignal(SIGPIPE, SIG_IGN);
|
||||
else
|
||||
pqsignal(SIGPIPE, SIG_DFL);
|
||||
/* Direct signals */
|
||||
if (pset->queryFoutPipe)
|
||||
pqsignal(SIGPIPE, SIG_IGN);
|
||||
else
|
||||
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
|
||||
* password interactively. Reads from stdin.
|
||||
*
|
||||
* prompt: The prompt to print
|
||||
* maxlen: How many characters to accept
|
||||
* echo: Set to false if you want to hide what is entered (for passwords)
|
||||
* prompt: The prompt to print
|
||||
* maxlen: How many characters to accept
|
||||
* 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).
|
||||
*/
|
||||
char *
|
||||
simple_prompt(const char *prompt, int maxlen, bool echo)
|
||||
{
|
||||
int length;
|
||||
char * destination;
|
||||
int length;
|
||||
char *destination;
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
struct termios t_orig, t;
|
||||
struct termios t_orig,
|
||||
t;
|
||||
|
||||
#endif
|
||||
|
||||
destination = (char *) malloc(maxlen+2);
|
||||
if (!destination)
|
||||
return NULL;
|
||||
if (prompt) fputs(prompt, stdout);
|
||||
destination = (char *) malloc(maxlen + 2);
|
||||
if (!destination)
|
||||
return NULL;
|
||||
if (prompt)
|
||||
fputs(prompt, stdout);
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
if (!echo)
|
||||
{
|
||||
tcgetattr(0, &t);
|
||||
t_orig = t;
|
||||
t.c_lflag &= ~ECHO;
|
||||
tcsetattr(0, TCSADRAIN, &t);
|
||||
}
|
||||
if (!echo)
|
||||
{
|
||||
tcgetattr(0, &t);
|
||||
t_orig = t;
|
||||
t.c_lflag &= ~ECHO;
|
||||
tcsetattr(0, TCSADRAIN, &t);
|
||||
}
|
||||
#endif
|
||||
|
||||
fgets(destination, maxlen, stdin);
|
||||
fgets(destination, maxlen, stdin);
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
if (!echo) {
|
||||
tcsetattr(0, TCSADRAIN, &t_orig);
|
||||
puts("");
|
||||
}
|
||||
if (!echo)
|
||||
{
|
||||
tcsetattr(0, TCSADRAIN, &t_orig);
|
||||
puts("");
|
||||
}
|
||||
#endif
|
||||
|
||||
length = strlen(destination);
|
||||
if (length > 0 && destination[length - 1] != '\n') {
|
||||
/* eat rest of the line */
|
||||
char buf[512];
|
||||
do {
|
||||
fgets(buf, 512, stdin);
|
||||
} while (buf[strlen(buf) - 1] != '\n');
|
||||
}
|
||||
length = strlen(destination);
|
||||
if (length > 0 && destination[length - 1] != '\n')
|
||||
{
|
||||
/* eat rest of the line */
|
||||
char buf[512];
|
||||
|
||||
if (length > 0 && destination[length - 1] == '\n')
|
||||
/* remove trailing newline */
|
||||
destination[length - 1] = '\0';
|
||||
do
|
||||
{
|
||||
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,68 +217,79 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
|
|||
* immediate consumption.
|
||||
*/
|
||||
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
|
||||
assert(name);
|
||||
assert(pset);
|
||||
assert(name);
|
||||
assert(pset);
|
||||
#else
|
||||
if (!name || !pset) return NULL;
|
||||
if (!name || !pset)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
if (strspn(name, VALID_VARIABLE_CHARS) == strlen(name)) {
|
||||
var = GetVariable(pset->vars, name);
|
||||
if (var)
|
||||
return var;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
if (strspn(name, VALID_VARIABLE_CHARS) == strlen(name))
|
||||
{
|
||||
var = GetVariable(pset->vars, name);
|
||||
if (var)
|
||||
return var;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* otherwise return magic variable */
|
||||
|
||||
if (strcmp(name, "Database")==0) {
|
||||
if (PQdb(pset->db))
|
||||
return PQdb(pset->db);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
/*
|
||||
* (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, "User")==0) {
|
||||
if (PQuser(pset->db))
|
||||
return PQuser(pset->db);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
if (strcmp(name, "Database") == 0)
|
||||
{
|
||||
if (PQdb(pset->db))
|
||||
return PQdb(pset->db);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
if (strcmp(name, "Host")==0) {
|
||||
if (PQhost(pset->db))
|
||||
return PQhost(pset->db);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
if (strcmp(name, "User") == 0)
|
||||
{
|
||||
if (PQuser(pset->db))
|
||||
return PQuser(pset->db);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
if (strcmp(name, "Port")==0) {
|
||||
if (PQport(pset->db))
|
||||
return PQport(pset->db);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
if (strcmp(name, "Host") == 0)
|
||||
{
|
||||
if (PQhost(pset->db))
|
||||
return PQhost(pset->db);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
/* env vars (if env vars are all caps there should be no prob, otherwise
|
||||
you're on your own */
|
||||
if (strcmp(name, "Port") == 0)
|
||||
{
|
||||
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().
|
||||
*/
|
||||
|
||||
PGconn * cancelConn;
|
||||
PGconn *cancelConn;
|
||||
|
||||
#ifdef WIN32
|
||||
#define safe_write_stderr(String) fputs(s, stderr)
|
||||
|
@ -296,79 +319,84 @@ PGconn * cancelConn;
|
|||
static void
|
||||
handle_sigint(SIGNAL_ARGS)
|
||||
{
|
||||
/* accept signal if no connection */
|
||||
if (cancelConn == NULL)
|
||||
exit(1);
|
||||
/* Try to send cancel request */
|
||||
if (PQrequestCancel(cancelConn))
|
||||
safe_write_stderr("\nCANCEL request sent\n");
|
||||
else {
|
||||
safe_write_stderr("\nCould not send cancel request: ");
|
||||
safe_write_stderr(PQerrorMessage(cancelConn));
|
||||
}
|
||||
/* accept signal if no connection */
|
||||
if (cancelConn == NULL)
|
||||
exit(1);
|
||||
/* Try to send cancel request */
|
||||
if (PQrequestCancel(cancelConn))
|
||||
safe_write_stderr("\nCANCEL request sent\n");
|
||||
else
|
||||
{
|
||||
safe_write_stderr("\nCould not send cancel request: ");
|
||||
safe_write_stderr(PQerrorMessage(cancelConn));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* PSQLexec
|
||||
/*
|
||||
* PSQLexec
|
||||
*
|
||||
* 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).
|
||||
*/
|
||||
PGresult *
|
||||
PGresult *
|
||||
PSQLexec(PsqlSettings *pset, const char *query)
|
||||
{
|
||||
PGresult *res;
|
||||
const char * var;
|
||||
PGresult *res;
|
||||
const char *var;
|
||||
|
||||
if (!pset->db) {
|
||||
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;
|
||||
if (!pset->db)
|
||||
{
|
||||
fputs("You are not currently connected to a database.\n", stderr);
|
||||
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;
|
||||
}
|
||||
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 ||
|
||||
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
|
||||
SendQuery(PsqlSettings *pset, const char *query)
|
||||
{
|
||||
bool success = false;
|
||||
PGresult *results;
|
||||
PGnotify *notify;
|
||||
bool success = false;
|
||||
PGresult *results;
|
||||
PGnotify *notify;
|
||||
|
||||
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))
|
||||
if (!pset->db)
|
||||
{
|
||||
case PGRES_TUPLES_OK:
|
||||
if (pset->gfname)
|
||||
{
|
||||
PsqlSettings settings_copy = *pset;
|
||||
fputs("You are not currently connected to a database.\n", stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
settings_copy.queryFout = stdout;
|
||||
if (!setQFout(pset->gfname, &settings_copy)) {
|
||||
success = false;
|
||||
break;
|
||||
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:
|
||||
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 */
|
||||
setQFout(NULL, &settings_copy);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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;
|
||||
if (results)
|
||||
PQclear(results);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
#include "settings.h"
|
||||
|
||||
char *
|
||||
xstrdup(const char * string);
|
||||
xstrdup(const char *string);
|
||||
|
||||
bool
|
||||
setQFout(const char *fname, PsqlSettings *pset);
|
||||
setQFout(const char *fname, PsqlSettings *pset);
|
||||
|
||||
char *
|
||||
simple_prompt(const char *prompt, int maxlen, bool echo);
|
||||
simple_prompt(const char *prompt, int maxlen, bool echo);
|
||||
|
||||
const char *
|
||||
interpolate_var(const char * name, PsqlSettings * pset);
|
||||
interpolate_var(const char *name, PsqlSettings *pset);
|
||||
|
||||
PGresult *
|
||||
PSQLexec(PsqlSettings *pset, const char *query);
|
||||
PGresult *
|
||||
PSQLexec(PsqlSettings *pset, const char *query);
|
||||
|
||||
bool
|
||||
SendQuery(PsqlSettings *pset, const char *query);
|
||||
SendQuery(PsqlSettings *pset, const char *query);
|
||||
|
||||
#endif /* COMMON_H */
|
||||
#endif /* COMMON_H */
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h> /* for isatty */
|
||||
#include <unistd.h> /* for isatty */
|
||||
#else
|
||||
#include <io.h> /* I think */
|
||||
#include <io.h> /* I think */
|
||||
#endif
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
@ -33,137 +33,151 @@
|
|||
* returns a malloc'ed structure with the options, or NULL on parsing error
|
||||
*/
|
||||
|
||||
struct copy_options {
|
||||
char * table;
|
||||
char * file;
|
||||
bool from;
|
||||
bool binary;
|
||||
bool oids;
|
||||
char * delim;
|
||||
struct copy_options
|
||||
{
|
||||
char *table;
|
||||
char *file;
|
||||
bool from;
|
||||
bool binary;
|
||||
bool oids;
|
||||
char *delim;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
free_copy_options(struct copy_options * ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
free(ptr->table);
|
||||
free(ptr->file);
|
||||
free(ptr->delim);
|
||||
free(ptr);
|
||||
if (!ptr)
|
||||
return;
|
||||
free(ptr->table);
|
||||
free(ptr->file);
|
||||
free(ptr->delim);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
|
||||
static struct copy_options *
|
||||
parse_slash_copy(const char *args)
|
||||
{
|
||||
struct copy_options * result;
|
||||
char * line;
|
||||
char * token;
|
||||
bool error = false;
|
||||
char quote;
|
||||
struct copy_options *result;
|
||||
char *line;
|
||||
char *token;
|
||||
bool error = false;
|
||||
char quote;
|
||||
|
||||
line = xstrdup(args);
|
||||
line = xstrdup(args);
|
||||
|
||||
if (!(result = calloc(1, sizeof (struct copy_options)))) {
|
||||
perror("calloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
token = strtokx(line, " \t", "\"", '\\', "e, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
else {
|
||||
if (!quote && strcasecmp(token, "binary")==0) {
|
||||
result->binary = true;
|
||||
token = strtokx(NULL, " \t", "\"", '\\', "e, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
if (!(result = calloc(1, sizeof(struct copy_options))))
|
||||
{
|
||||
perror("calloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
token = strtokx(line, " \t", "\"", '\\', "e, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
else
|
||||
{
|
||||
if (!quote && strcasecmp(token, "binary") == 0)
|
||||
{
|
||||
result->binary = true;
|
||||
token = strtokx(NULL, " \t", "\"", '\\', "e, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
}
|
||||
if (token)
|
||||
result->table = xstrdup(token);
|
||||
}
|
||||
if (token)
|
||||
result->table = xstrdup(token);
|
||||
}
|
||||
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
assert(error || result->table);
|
||||
assert(error || result->table);
|
||||
#endif
|
||||
|
||||
if (!error) {
|
||||
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
else {
|
||||
if (strcasecmp(token, "with")==0) {
|
||||
if (!error)
|
||||
{
|
||||
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
|
||||
if (!token || strcasecmp(token, "oids")!=0)
|
||||
error = true;
|
||||
if (!token)
|
||||
error = true;
|
||||
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) {
|
||||
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
if (!error)
|
||||
{
|
||||
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
|
||||
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) {
|
||||
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
else
|
||||
result->file=xstrdup(token);
|
||||
}
|
||||
if (!error)
|
||||
{
|
||||
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
|
||||
if (!token)
|
||||
error = true;
|
||||
else
|
||||
result->file = xstrdup(token);
|
||||
}
|
||||
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
assert(error || result->file);
|
||||
assert(error || result->file);
|
||||
#endif
|
||||
|
||||
if (!error) {
|
||||
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
|
||||
if (token) {
|
||||
if (strcasecmp(token, "using")!=0)
|
||||
error = true;
|
||||
else {
|
||||
if (!error)
|
||||
{
|
||||
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
|
||||
if (!token || strcasecmp(token, "delimiters")!=0)
|
||||
error = true;
|
||||
else {
|
||||
token = strtokx(NULL, " \t", "'", '\\', NULL, NULL);
|
||||
if (token)
|
||||
result->delim = xstrdup(token);
|
||||
else
|
||||
error = true;
|
||||
if (token)
|
||||
{
|
||||
if (strcasecmp(token, "using") != 0)
|
||||
error = true;
|
||||
else
|
||||
{
|
||||
token = strtokx(NULL, " \t", NULL, '\\', NULL, NULL);
|
||||
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) {
|
||||
fputs("Parse error at ", stderr);
|
||||
if (!token)
|
||||
fputs("end of line.", stderr);
|
||||
if (error)
|
||||
{
|
||||
fputs("Parse error at ", stderr);
|
||||
if (!token)
|
||||
fputs("end of line.", stderr);
|
||||
else
|
||||
fprintf(stderr, "'%s'.", token);
|
||||
fputs("\n", stderr);
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "'%s'.", token);
|
||||
fputs("\n", stderr);
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -171,103 +185,109 @@ parse_slash_copy(const char *args)
|
|||
* Execute a \copy command (frontend copy). We have to open a file, then
|
||||
* submit a COPY query to the backend and either feed it data from the
|
||||
* file or route its response into the file.
|
||||
*/
|
||||
*/
|
||||
bool
|
||||
do_copy(const char * args, PsqlSettings *pset)
|
||||
do_copy(const char *args, PsqlSettings *pset)
|
||||
{
|
||||
char query[128 + NAMEDATALEN];
|
||||
FILE *copystream;
|
||||
struct copy_options *options;
|
||||
PGresult *result;
|
||||
bool success;
|
||||
char query[128 + NAMEDATALEN];
|
||||
FILE *copystream;
|
||||
struct copy_options *options;
|
||||
PGresult *result;
|
||||
bool success;
|
||||
|
||||
/* parse options */
|
||||
options = parse_slash_copy(args);
|
||||
/* parse options */
|
||||
options = parse_slash_copy(args);
|
||||
|
||||
if (!options)
|
||||
return false;
|
||||
if (!options)
|
||||
return false;
|
||||
|
||||
strcpy(query, "COPY ");
|
||||
if (options->binary)
|
||||
fputs("Warning: \\copy binary is not implemented. Resorting to text output.\n", stderr);
|
||||
strcpy(query, "COPY ");
|
||||
if (options->binary)
|
||||
fputs("Warning: \\copy binary is not implemented. Resorting to text output.\n", stderr);
|
||||
/* strcat(query, "BINARY "); */
|
||||
|
||||
strcat(query, "\"");
|
||||
strncat(query, options->table, NAMEDATALEN);
|
||||
strcat(query, "\" ");
|
||||
if (options->oids)
|
||||
strcat(query, "WITH OIDS ");
|
||||
strcat(query, "\"");
|
||||
strncat(query, options->table, NAMEDATALEN);
|
||||
strcat(query, "\" ");
|
||||
if (options->oids)
|
||||
strcat(query, "WITH OIDS ");
|
||||
|
||||
if (options->from)
|
||||
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.");
|
||||
if (options->from)
|
||||
strcat(query, "FROM stdin");
|
||||
else
|
||||
puts("Copy failed.");
|
||||
}
|
||||
strcat(query, "TO stdout");
|
||||
|
||||
fclose(copystream);
|
||||
free_copy_options(options);
|
||||
return success;
|
||||
|
||||
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
|
||||
puts("Copy failed.");
|
||||
}
|
||||
|
||||
fclose(copystream);
|
||||
free_copy_options(options);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
@ -287,38 +307,38 @@ do_copy(const char * args, PsqlSettings *pset)
|
|||
bool
|
||||
handleCopyOut(PGconn *conn, FILE *copystream)
|
||||
{
|
||||
bool copydone = false; /* haven't started yet */
|
||||
char copybuf[COPYBUFSIZ];
|
||||
int ret;
|
||||
bool copydone = false; /* haven't started yet */
|
||||
char copybuf[COPYBUFSIZ];
|
||||
int ret;
|
||||
|
||||
while (!copydone)
|
||||
{
|
||||
ret = PQgetline(conn, copybuf, COPYBUFSIZ);
|
||||
while (!copydone)
|
||||
{
|
||||
ret = PQgetline(conn, copybuf, COPYBUFSIZ);
|
||||
|
||||
if (copybuf[0] == '\\' &&
|
||||
copybuf[1] == '.' &&
|
||||
copybuf[2] == '\0')
|
||||
{
|
||||
copydone = true; /* we're at the end */
|
||||
if (copybuf[0] == '\\' &&
|
||||
copybuf[1] == '.' &&
|
||||
copybuf[2] == '\0')
|
||||
{
|
||||
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
|
||||
{
|
||||
fputs(copybuf, copystream);
|
||||
switch (ret)
|
||||
{
|
||||
case EOF:
|
||||
copydone = true;
|
||||
/* FALLTHROUGH */
|
||||
case 0:
|
||||
fputc('\n', copystream);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fflush(copystream);
|
||||
return !PQendcopy(conn);
|
||||
fflush(copystream);
|
||||
return !PQendcopy(conn);
|
||||
}
|
||||
|
||||
|
||||
|
@ -333,58 +353,58 @@ handleCopyOut(PGconn *conn, FILE *copystream)
|
|||
* (and which gave you PGRES_COPY_IN back);
|
||||
* copystream is the file stream you want the input to come from
|
||||
* 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
|
||||
handleCopyIn(PGconn *conn, FILE *copystream, const char * prompt)
|
||||
handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt)
|
||||
{
|
||||
bool copydone = false;
|
||||
bool firstload;
|
||||
bool linedone;
|
||||
char copybuf[COPYBUFSIZ];
|
||||
char *s;
|
||||
int buflen;
|
||||
int c = 0;
|
||||
bool copydone = false;
|
||||
bool firstload;
|
||||
bool linedone;
|
||||
char copybuf[COPYBUFSIZ];
|
||||
char *s;
|
||||
int buflen;
|
||||
int c = 0;
|
||||
|
||||
while (!copydone)
|
||||
{ /* for each input line ... */
|
||||
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)
|
||||
while (!copydone)
|
||||
{ /* for each input line ... */
|
||||
if (prompt && isatty(fileno(stdin)))
|
||||
{
|
||||
linedone = true;
|
||||
break;
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
*s++ = c;
|
||||
}
|
||||
*s = '\0';
|
||||
if (c == EOF)
|
||||
{
|
||||
PQputline(conn, "\\.");
|
||||
copydone = true;
|
||||
break;
|
||||
}
|
||||
PQputline(conn, copybuf);
|
||||
if (firstload)
|
||||
{
|
||||
if (!strcmp(copybuf, "\\."))
|
||||
copydone = true;
|
||||
firstload = false;
|
||||
}
|
||||
firstload = true;
|
||||
linedone = false;
|
||||
while (!linedone)
|
||||
{ /* for each buffer ... */
|
||||
s = copybuf;
|
||||
for (buflen = COPYBUFSIZ; buflen > 1; buflen--)
|
||||
{
|
||||
c = getc(copystream);
|
||||
if (c == '\n' || c == EOF)
|
||||
{
|
||||
linedone = true;
|
||||
break;
|
||||
}
|
||||
*s++ = c;
|
||||
}
|
||||
*s = '\0';
|
||||
if (c == EOF)
|
||||
{
|
||||
PQputline(conn, "\\.");
|
||||
copydone = true;
|
||||
break;
|
||||
}
|
||||
PQputline(conn, copybuf);
|
||||
if (firstload)
|
||||
{
|
||||
if (!strcmp(copybuf, "\\."))
|
||||
copydone = true;
|
||||
firstload = false;
|
||||
}
|
||||
}
|
||||
PQputline(conn, "\n");
|
||||
}
|
||||
PQputline(conn, "\n");
|
||||
}
|
||||
return !PQendcopy(conn);
|
||||
return !PQendcopy(conn);
|
||||
}
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
/* handler for \copy */
|
||||
bool
|
||||
do_copy(const char *args, PsqlSettings *pset);
|
||||
do_copy(const char *args, PsqlSettings *pset);
|
||||
|
||||
|
||||
/* lower level processors for copy in/out streams */
|
||||
|
||||
bool
|
||||
handleCopyOut(PGconn *conn, FILE *copystream);
|
||||
handleCopyOut(PGconn *conn, FILE *copystream);
|
||||
|
||||
bool
|
||||
handleCopyIn(PGconn *conn, FILE *copystream, const char * prompt);
|
||||
handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt);
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,38 +5,38 @@
|
|||
|
||||
/* \da */
|
||||
bool
|
||||
describeAggregates(const char * name, PsqlSettings * pset);
|
||||
describeAggregates(const char *name, PsqlSettings *pset);
|
||||
|
||||
/* \df */
|
||||
bool
|
||||
describeFunctions(const char * name, PsqlSettings * pset);
|
||||
describeFunctions(const char *name, PsqlSettings *pset);
|
||||
|
||||
/* \dT */
|
||||
bool
|
||||
describeTypes(const char * name, PsqlSettings * pset);
|
||||
describeTypes(const char *name, PsqlSettings *pset);
|
||||
|
||||
/* \do */
|
||||
bool
|
||||
describeOperators(const char * name, PsqlSettings * pset);
|
||||
describeOperators(const char *name, PsqlSettings *pset);
|
||||
|
||||
/* \dp (formerly \z) */
|
||||
bool
|
||||
permissionsList(const char * name, PsqlSettings *pset);
|
||||
permissionsList(const char *name, PsqlSettings *pset);
|
||||
|
||||
/* \dd */
|
||||
bool
|
||||
objectDescription(const char * object, PsqlSettings *pset);
|
||||
objectDescription(const char *object, PsqlSettings *pset);
|
||||
|
||||
/* \d foo */
|
||||
bool
|
||||
describeTableDetails(const char * name, PsqlSettings * pset);
|
||||
describeTableDetails(const char *name, PsqlSettings *pset);
|
||||
|
||||
/* \l */
|
||||
bool
|
||||
listAllDbs(PsqlSettings *pset);
|
||||
listAllDbs(PsqlSettings *pset);
|
||||
|
||||
/* \dt, \di, \dS, etc. */
|
||||
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 */
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
#include <signal.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/ioctl.h> /* for ioctl() */
|
||||
#include <sys/ioctl.h> /* for ioctl() */
|
||||
#ifdef HAVE_PWD_H
|
||||
#include <pwd.h> /* for getpwuid() */
|
||||
#include <pwd.h> /* for getpwuid() */
|
||||
#endif
|
||||
#include <sys/types.h> /* (ditto) */
|
||||
#include <unistd.h> /* for getuid() */
|
||||
#include <sys/types.h> /* (ditto) */
|
||||
#include <unistd.h> /* for getuid() */
|
||||
#else
|
||||
#define strcasecmp(x,y) stricmp(x,y)
|
||||
#define popen(x,y) _popen(x,y)
|
||||
|
@ -34,85 +34,94 @@
|
|||
*/
|
||||
#define ON(var) (var ? "on" : "off")
|
||||
|
||||
void usage(void)
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
const char *env;
|
||||
const char *user;
|
||||
const char *env;
|
||||
const char *user;
|
||||
|
||||
#ifndef WIN32
|
||||
struct passwd *pw = NULL;
|
||||
struct passwd *pw = NULL;
|
||||
|
||||
#endif
|
||||
|
||||
/* Find default user, in case we need it. */
|
||||
user = getenv("USER");
|
||||
if (!user) {
|
||||
/* Find default user, in case we need it. */
|
||||
user = getenv("USER");
|
||||
if (!user)
|
||||
{
|
||||
#ifndef WIN32
|
||||
pw = getpwuid(getuid());
|
||||
if (pw) user = pw->pw_name;
|
||||
else {
|
||||
perror("getpwuid()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
pw = getpwuid(getuid());
|
||||
if (pw)
|
||||
user = pw->pw_name;
|
||||
else
|
||||
{
|
||||
perror("getpwuid()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#else
|
||||
user = "?";
|
||||
user = "?";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* 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, " -A Unaligned table output mode (-P format=unaligned)\n");
|
||||
fprintf(stderr, " -c query Run single query (slash commands, too) and exit\n");
|
||||
fprintf(stderr, "Usage: psql [options] [dbname [username]] \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");
|
||||
|
||||
/* Display default database */
|
||||
env = getenv("PGDATABASE");
|
||||
if (!env) env=user;
|
||||
fprintf(stderr, " -d dbname Specify database name to connect to (default: %s)\n", env);
|
||||
/* Display default database */
|
||||
env = getenv("PGDATABASE");
|
||||
if (!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 Display queries that internal commands generate\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, " -e Echo all input in non-interactive mode\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 sep Set field separator (default: '" DEFAULT_FIELD_SEP "') (-P fieldsep=)\n");
|
||||
|
||||
/* Display default host */
|
||||
env = getenv("PGHOST");
|
||||
fprintf(stderr, " -h host Specify database server host (default: ");
|
||||
if (env)
|
||||
fprintf(stderr, env);
|
||||
else
|
||||
fprintf(stderr, "domain socket");
|
||||
fprintf(stderr, ")\n");
|
||||
/* Display default host */
|
||||
env = getenv("PGHOST");
|
||||
fprintf(stderr, " -h host Specify database server host (default: ");
|
||||
if (env)
|
||||
fprintf(stderr, env);
|
||||
else
|
||||
fprintf(stderr, "domain socket");
|
||||
fprintf(stderr, ")\n");
|
||||
|
||||
fprintf(stderr, " -H HTML table output mode (-P format=html)\n");
|
||||
fprintf(stderr, " -l List available databases, then exit\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, " -H HTML table output mode (-P format=html)\n");
|
||||
fprintf(stderr, " -l List available databases, then exit\n");
|
||||
fprintf(stderr, " -n Do not use readline and history\n");
|
||||
fprintf(stderr, " -o filename Send query output to filename (or |pipe)\n");
|
||||
|
||||
/* Display default port */
|
||||
env = getenv("PGPORT");
|
||||
fprintf(stderr, " -p port Specify database server port (default: %s)\n",
|
||||
env ? env : "hardwired");
|
||||
/* Display default port */
|
||||
env = getenv("PGPORT");
|
||||
fprintf(stderr, " -p port Specify database server port (default: %s)\n",
|
||||
env ? env : "hardwired");
|
||||
|
||||
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, " -s Single step mode (confirm each 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 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, " -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, " -s Single step mode (confirm each 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 text Set HTML table tag options (e.g., width, border)\n");
|
||||
fprintf(stderr, " -u Prompt for username and password (same as \"-U ? -W\")\n");
|
||||
|
||||
/* Display default user */
|
||||
env = getenv("PGUSER");
|
||||
if (!env) env=user;
|
||||
fprintf(stderr, " -U [username] Specifiy username, \"?\"=prompt (default user: %s)\n", env);
|
||||
/* Display default user */
|
||||
env = getenv("PGUSER");
|
||||
if (!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, " -v name=val Set psql variable 'name' to 'value'\n");
|
||||
fprintf(stderr, " -V Show version information and exit\n");
|
||||
fprintf(stderr, " -W Prompt for password (should happen automatically)\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 Show version information and exit\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
|
||||
if (pw) free(pw);
|
||||
if (pw)
|
||||
free(pw);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -125,82 +134,85 @@ void usage(void)
|
|||
*/
|
||||
|
||||
#ifndef TIOCGWINSZ
|
||||
struct winsize {
|
||||
int ws_row;
|
||||
int ws_col;
|
||||
struct winsize
|
||||
{
|
||||
int ws_row;
|
||||
int ws_col;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
slashUsage(PsqlSettings *pset)
|
||||
{
|
||||
bool usePipe = false;
|
||||
const char *pagerenv;
|
||||
FILE *fout;
|
||||
struct winsize screen_size;
|
||||
bool usePipe = false;
|
||||
const char *pagerenv;
|
||||
FILE *fout;
|
||||
struct winsize screen_size;
|
||||
|
||||
#ifdef TIOCGWINSZ
|
||||
if (pset->notty == 0 &&
|
||||
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
|
||||
screen_size.ws_col == 0 ||
|
||||
screen_size.ws_row == 0))
|
||||
{
|
||||
if (pset->notty == 0 &&
|
||||
(ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
|
||||
screen_size.ws_col == 0 ||
|
||||
screen_size.ws_row == 0))
|
||||
{
|
||||
#endif
|
||||
screen_size.ws_row = 24;
|
||||
screen_size.ws_col = 80;
|
||||
screen_size.ws_row = 24;
|
||||
screen_size.ws_col = 80;
|
||||
#ifdef TIOCGWINSZ
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pset->notty == 0 &&
|
||||
(pagerenv = getenv("PAGER")) &&
|
||||
(pagerenv[0] != '\0') &&
|
||||
screen_size.ws_row <= 36 &&
|
||||
(fout = popen(pagerenv, "w")))
|
||||
{
|
||||
usePipe = true;
|
||||
pqsignal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
else
|
||||
fout = stdout;
|
||||
if (pset->notty == 0 &&
|
||||
(pagerenv = getenv("PAGER")) &&
|
||||
(pagerenv[0] != '\0') &&
|
||||
screen_size.ws_row <= 36 &&
|
||||
(fout = popen(pagerenv, "w")))
|
||||
{
|
||||
usePipe = true;
|
||||
pqsignal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
else
|
||||
fout = stdout;
|
||||
|
||||
/* if you add/remove a line here, change the row test above */
|
||||
fprintf(fout, " \\? -- help\n");
|
||||
fprintf(fout, " \\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, " \\copyright -- show PostgreSQL copyright\n");
|
||||
fprintf(fout, " \\d -- list tables, views, and sequences\n");
|
||||
fprintf(fout, " \\distvS -- list only indices/sequences/tables/views/system tables\n");
|
||||
fprintf(fout, " \\da -- list aggregates\n");
|
||||
fprintf(fout, " \\dd [<object>]- list comment for table, type, function, or operator\n");
|
||||
fprintf(fout, " \\df -- list functions\n");
|
||||
fprintf(fout, " \\do -- list operators\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, " \\echo <text> -- write text to stdout\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, " \\i <fname> -- read and execute queries from filename\n");
|
||||
fprintf(fout, " \\l -- list all databases\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, " \\p -- print the content of the current query buffer\n");
|
||||
fprintf(fout, " \\pset -- set table output options\n");
|
||||
fprintf(fout, " \\q -- quit\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, " \\s [<fname>] -- print history or save it in <fname>\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, " \\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, " \\z -- list table access permissions\n");
|
||||
fprintf(fout, " \\! [<cmd>] -- shell escape or command\n");
|
||||
/* if you add/remove a line here, change the row test above */
|
||||
fprintf(fout, " \\? -- help\n");
|
||||
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, " \\copyright -- show PostgreSQL copyright\n");
|
||||
fprintf(fout, " \\d -- list tables, views, and sequences\n");
|
||||
fprintf(fout, " \\distvS -- list only indices/sequences/tables/views/system tables\n");
|
||||
fprintf(fout, " \\da -- list aggregates\n");
|
||||
fprintf(fout, " \\dd [<object>]- list comment for table, type, function, or operator\n");
|
||||
fprintf(fout, " \\df -- list functions\n");
|
||||
fprintf(fout, " \\do -- list operators\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, " \\echo <text> -- write text to stdout\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, " \\i <fname> -- read and execute queries from filename\n");
|
||||
fprintf(fout, " \\l -- list all databases\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, " \\p -- print the content of the current query buffer\n");
|
||||
fprintf(fout, " \\pset -- set table output options\n");
|
||||
fprintf(fout, " \\q -- quit\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, " \\s [<fname>] -- print history or save it in <fname>\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, " \\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, " \\z -- list table access permissions\n");
|
||||
fprintf(fout, " \\! [<cmd>] -- shell escape or command\n");
|
||||
|
||||
if (usePipe) {
|
||||
pclose(fout);
|
||||
pqsignal(SIGPIPE, SIG_DFL);
|
||||
}
|
||||
if (usePipe)
|
||||
{
|
||||
pclose(fout);
|
||||
pqsignal(SIGPIPE, SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -212,59 +224,59 @@ slashUsage(PsqlSettings *pset)
|
|||
void
|
||||
helpSQL(const char *topic)
|
||||
{
|
||||
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)
|
||||
if (!topic || strlen(topic) == 0)
|
||||
{
|
||||
switch (left_center_right)
|
||||
{
|
||||
case 'L':
|
||||
printf(" %-25s", QL_HELP[i].cmd);
|
||||
left_center_right = 'C';
|
||||
break;
|
||||
case 'C':
|
||||
printf("%-25s", QL_HELP[i].cmd);
|
||||
left_center_right = 'R';
|
||||
break;
|
||||
case 'R':
|
||||
printf("%-25s\n", QL_HELP[i].cmd);
|
||||
left_center_right = 'L';
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (left_center_right != 'L')
|
||||
puts("\n");
|
||||
puts("Or type \\h * for a complete description of all commands.");
|
||||
}
|
||||
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:");
|
||||
|
||||
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);
|
||||
}
|
||||
left_center_right = 'L';/* Start with left column */
|
||||
i = 0;
|
||||
while (QL_HELP[i].cmd != NULL)
|
||||
{
|
||||
switch (left_center_right)
|
||||
{
|
||||
case 'L':
|
||||
printf(" %-25s", QL_HELP[i].cmd);
|
||||
left_center_right = 'C';
|
||||
break;
|
||||
case 'C':
|
||||
printf("%-25s", QL_HELP[i].cmd);
|
||||
left_center_right = 'R';
|
||||
break;
|
||||
case 'R':
|
||||
printf("%-25s\n", QL_HELP[i].cmd);
|
||||
left_center_right = 'L';
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (left_center_right != 'L')
|
||||
puts("\n");
|
||||
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
|
||||
print_copyright(void)
|
||||
{
|
||||
puts(
|
||||
"
|
||||
PostgreSQL Data Base Management System
|
||||
puts(
|
||||
"
|
||||
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
|
||||
contains the following notice:
|
||||
This software is based on Postgres95, formerly known as Postgres, which
|
||||
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
|
||||
documentation for any purpose, without fee, and without a written agreement
|
||||
is hereby granted, provided that the above copyright notice and this paragraph
|
||||
and the following two paragraphs appear in all copies.
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose, without fee, and without a written agreement
|
||||
is hereby granted, provided that the above copyright notice and this paragraph
|
||||
and the following two paragraphs appear in all copies.
|
||||
|
||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
|
||||
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
|
||||
DAMAGE.
|
||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
|
||||
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
|
||||
DAMAGE.
|
||||
|
||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
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,
|
||||
AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
|
||||
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
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,
|
||||
AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
|
||||
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
|
||||
(end of terms)"
|
||||
(end of terms) "
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
|
||||
#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
|
||||
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
/* (of course there is no runtime command for doing that :) */
|
||||
#ifdef USE_READLINE
|
||||
static bool useReadline;
|
||||
|
||||
#endif
|
||||
#ifdef USE_HISTORY
|
||||
static bool useHistory;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -25,29 +27,31 @@ static bool useHistory;
|
|||
char *
|
||||
gets_interactive(const char *prompt)
|
||||
{
|
||||
char * s;
|
||||
char *s;
|
||||
|
||||
#ifdef USE_READLINE
|
||||
if (useReadline) {
|
||||
s = readline(prompt);
|
||||
fputc('\r', stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
else {
|
||||
if (useReadline)
|
||||
{
|
||||
s = readline(prompt);
|
||||
fputc('\r', stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout);
|
||||
s = gets_fromFile(stdin);
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout);
|
||||
s = gets_fromFile(stdin);
|
||||
#ifdef USE_READLINE
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_HISTORY
|
||||
if (useHistory && s && s[0] != '\0')
|
||||
add_history(s);
|
||||
if (useHistory && s && s[0] != '\0')
|
||||
add_history(s);
|
||||
#endif
|
||||
|
||||
return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,25 +64,27 @@ gets_interactive(const char *prompt)
|
|||
char *
|
||||
gets_fromFile(FILE *source)
|
||||
{
|
||||
PQExpBufferData buffer;
|
||||
char line[1024];
|
||||
PQExpBufferData buffer;
|
||||
char line[1024];
|
||||
|
||||
initPQExpBuffer(&buffer);
|
||||
initPQExpBuffer(&buffer);
|
||||
|
||||
while (fgets(line, 1024, source) != NULL) {
|
||||
appendPQExpBufferStr(&buffer, line);
|
||||
if (buffer.data[buffer.len-1] == '\n') {
|
||||
buffer.data[buffer.len-1] = '\0';
|
||||
return buffer.data;
|
||||
while (fgets(line, 1024, source) != NULL)
|
||||
{
|
||||
appendPQExpBufferStr(&buffer, line);
|
||||
if (buffer.data[buffer.len - 1] == '\n')
|
||||
{
|
||||
buffer.data[buffer.len - 1] = '\0';
|
||||
return buffer.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.len > 0)
|
||||
return buffer.data; /* EOF after reading some bufferload(s) */
|
||||
if (buffer.len > 0)
|
||||
return buffer.data; /* EOF after reading some bufferload(s) */
|
||||
|
||||
/* EOF, so return null */
|
||||
termPQExpBuffer(&buffer);
|
||||
return NULL;
|
||||
/* EOF, so return null */
|
||||
termPQExpBuffer(&buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -93,28 +99,33 @@ void
|
|||
initializeInput(int flags)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
if (flags == 1) {
|
||||
useReadline = true;
|
||||
rl_readline_name = "psql";
|
||||
}
|
||||
if (flags == 1)
|
||||
{
|
||||
useReadline = true;
|
||||
rl_readline_name = "psql";
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_HISTORY
|
||||
if (flags == 1) {
|
||||
const char * home;
|
||||
if (flags == 1)
|
||||
{
|
||||
const char *home;
|
||||
|
||||
useHistory = true;
|
||||
using_history();
|
||||
home = getenv("HOME");
|
||||
if (home) {
|
||||
char * psql_history = (char *) malloc(strlen(home) + 20);
|
||||
if (psql_history) {
|
||||
sprintf(psql_history, "%s/.psql_history", home);
|
||||
read_history(psql_history);
|
||||
free(psql_history);
|
||||
}
|
||||
useHistory = true;
|
||||
using_history();
|
||||
home = getenv("HOME");
|
||||
if (home)
|
||||
{
|
||||
char *psql_history = (char *) malloc(strlen(home) + 20);
|
||||
|
||||
if (psql_history)
|
||||
{
|
||||
sprintf(psql_history, "%s/.psql_history", home);
|
||||
read_history(psql_history);
|
||||
free(psql_history);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -124,17 +135,19 @@ bool
|
|||
saveHistory(const char *fname)
|
||||
{
|
||||
#ifdef USE_HISTORY
|
||||
if (useHistory) {
|
||||
if (write_history(fname) != 0) {
|
||||
perror(fname);
|
||||
return false;
|
||||
if (useHistory)
|
||||
{
|
||||
if (write_history(fname) != 0)
|
||||
{
|
||||
perror(fname);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
else
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -144,19 +157,22 @@ void
|
|||
finishInput(void)
|
||||
{
|
||||
#ifdef USE_HISTORY
|
||||
if (useHistory) {
|
||||
char * home;
|
||||
char * psql_history;
|
||||
if (useHistory)
|
||||
{
|
||||
char *home;
|
||||
char *psql_history;
|
||||
|
||||
home = getenv("HOME");
|
||||
if (home) {
|
||||
psql_history = (char *) malloc(strlen(home) + 20);
|
||||
if (psql_history) {
|
||||
sprintf(psql_history, "%s/.psql_history", home);
|
||||
write_history(psql_history);
|
||||
free(psql_history);
|
||||
}
|
||||
home = getenv("HOME");
|
||||
if (home)
|
||||
{
|
||||
psql_history = (char *) malloc(strlen(home) + 20);
|
||||
if (psql_history)
|
||||
{
|
||||
sprintf(psql_history, "%s/.psql_history", home);
|
||||
write_history(psql_history);
|
||||
free(psql_history);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -38,19 +38,19 @@
|
|||
|
||||
|
||||
char *
|
||||
gets_interactive(const char *prompt);
|
||||
gets_interactive(const char *prompt);
|
||||
|
||||
char *
|
||||
gets_fromFile(FILE *source);
|
||||
gets_fromFile(FILE *source);
|
||||
|
||||
|
||||
void
|
||||
initializeInput(int flags);
|
||||
initializeInput(int flags);
|
||||
|
||||
bool
|
||||
saveHistory(const char *fname);
|
||||
saveHistory(const char *fname);
|
||||
|
||||
void
|
||||
finishInput(void);
|
||||
finishInput(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "print.h"
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* Since all large object ops must be in a transaction, we must do some magic
|
||||
* here. You can set the variable lo_transaction to one of commit|rollback|
|
||||
* nothing to get your favourite behaviour regarding any transaction in
|
||||
|
@ -24,47 +24,50 @@
|
|||
static char notice[80];
|
||||
|
||||
static void
|
||||
_my_notice_handler(void * arg, const char * message) {
|
||||
(void)arg;
|
||||
strncpy(notice, message, 79);
|
||||
notice[79] = '\0';
|
||||
_my_notice_handler(void *arg, const char *message)
|
||||
{
|
||||
(void) arg;
|
||||
strncpy(notice, message, 79);
|
||||
notice[79] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
handle_transaction(PsqlSettings * pset)
|
||||
handle_transaction(PsqlSettings *pset)
|
||||
{
|
||||
const char * var = GetVariable(pset->vars, "lo_transaction");
|
||||
PGresult * res;
|
||||
bool commit;
|
||||
PQnoticeProcessor old_notice_hook;
|
||||
const char *var = GetVariable(pset->vars, "lo_transaction");
|
||||
PGresult *res;
|
||||
bool commit;
|
||||
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;
|
||||
|
||||
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
|
||||
*/
|
||||
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;
|
||||
int status;
|
||||
bool own_transaction = true;
|
||||
const char * var = GetVariable(pset->vars, "lo_transaction");
|
||||
PGresult *res;
|
||||
int status;
|
||||
bool own_transaction = true;
|
||||
const char *var = GetVariable(pset->vars, "lo_transaction");
|
||||
|
||||
if (var && strcmp(var, "nothing")==0)
|
||||
own_transaction = false;
|
||||
if (var && strcmp(var, "nothing") == 0)
|
||||
own_transaction = false;
|
||||
|
||||
if (!pset->db) {
|
||||
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;
|
||||
if (!pset->db)
|
||||
{
|
||||
fputs("You are not connected to a database.\n", stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
}
|
||||
if (own_transaction)
|
||||
{
|
||||
if (!handle_transaction(pset))
|
||||
return false;
|
||||
|
||||
fprintf(pset->queryFout, "lo_export\n");
|
||||
|
||||
return true;
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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;
|
||||
Oid loid;
|
||||
char buf[1024];
|
||||
unsigned int i;
|
||||
bool own_transaction = true;
|
||||
const char * var = GetVariable(pset->vars, "lo_transaction");
|
||||
PGresult *res;
|
||||
Oid loid;
|
||||
char buf[1024];
|
||||
unsigned int i;
|
||||
bool own_transaction = true;
|
||||
const char *var = GetVariable(pset->vars, "lo_transaction");
|
||||
|
||||
if (var && strcmp(var, "nothing")==0)
|
||||
own_transaction = false;
|
||||
if (var && strcmp(var, "nothing") == 0)
|
||||
own_transaction = false;
|
||||
|
||||
if (!pset->db) {
|
||||
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);
|
||||
if (!pset->db)
|
||||
{
|
||||
fputs("You are not connected to a database.\n", stderr);
|
||||
return false;
|
||||
}
|
||||
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 (own_transaction)
|
||||
{
|
||||
if (!handle_transaction(pset))
|
||||
return false;
|
||||
|
||||
if (!(res = PSQLexec(pset, "BEGIN")))
|
||||
return false;
|
||||
|
||||
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);
|
||||
}
|
||||
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
|
||||
*/
|
||||
bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
|
||||
bool
|
||||
do_lo_unlink(PsqlSettings *pset, const char *loid_arg)
|
||||
{
|
||||
PGresult * res;
|
||||
int status;
|
||||
Oid loid = (Oid)atol(loid_arg);
|
||||
char buf[256];
|
||||
bool own_transaction = true;
|
||||
const char * var = GetVariable(pset->vars, "lo_transaction");
|
||||
PGresult *res;
|
||||
int status;
|
||||
Oid loid = (Oid) atol(loid_arg);
|
||||
char buf[256];
|
||||
bool own_transaction = true;
|
||||
const char *var = GetVariable(pset->vars, "lo_transaction");
|
||||
|
||||
if (var && strcmp(var, "nothing")==0)
|
||||
own_transaction = false;
|
||||
if (var && strcmp(var, "nothing") == 0)
|
||||
own_transaction = false;
|
||||
|
||||
if (!pset->db) {
|
||||
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);
|
||||
if (!pset->db)
|
||||
{
|
||||
fputs("You are not connected to a database.\n", stderr);
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
if (own_transaction)
|
||||
{
|
||||
if (!handle_transaction(pset))
|
||||
return false;
|
||||
|
||||
if (!(res = PSQLexec(pset, "BEGIN")))
|
||||
return false;
|
||||
|
||||
PQclear(res);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (own_transaction) {
|
||||
if (!(res = PSQLexec(pset, "COMMIT"))) {
|
||||
res = PQexec(pset->db, "ROLLBACK");
|
||||
PQclear(res);
|
||||
return false;
|
||||
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 */
|
||||
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
|
||||
*/
|
||||
bool do_lo_list(PsqlSettings * pset)
|
||||
bool
|
||||
do_lo_list(PsqlSettings *pset)
|
||||
{
|
||||
PGresult * res;
|
||||
char descbuf[512];
|
||||
printQueryOpt myopt = pset->popt;
|
||||
PGresult *res;
|
||||
char descbuf[512];
|
||||
printQueryOpt myopt = pset->popt;
|
||||
|
||||
descbuf[0] = '\0';
|
||||
strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
|
||||
if (GetVariableBool(pset->vars, "description"))
|
||||
strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\"");
|
||||
strcat(descbuf,"\nFROM pg_class, pg_user\n"
|
||||
descbuf[0] = '\0';
|
||||
strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
|
||||
if (GetVariableBool(pset->vars, "description"))
|
||||
strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\"");
|
||||
strcat(descbuf, "\nFROM pg_class, pg_user\n"
|
||||
"WHERE usesysid = relowner AND relkind = 'l'\n"
|
||||
"ORDER BY \"ID\"");
|
||||
|
||||
res = PSQLexec(pset, descbuf);
|
||||
if (!res)
|
||||
return false;
|
||||
res = PSQLexec(pset, descbuf);
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
myopt.topt.tuples_only = false;
|
||||
myopt.nullPrint = NULL;
|
||||
myopt.title = "Large objects";
|
||||
myopt.topt.tuples_only = false;
|
||||
myopt.nullPrint = NULL;
|
||||
myopt.title = "Large objects";
|
||||
|
||||
printQuery(res, &myopt, pset->queryFout);
|
||||
printQuery(res, &myopt, pset->queryFout);
|
||||
|
||||
PQclear(res);
|
||||
return true;
|
||||
PQclear(res);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
#include "settings.h"
|
||||
|
||||
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_unlink(PsqlSettings * pset, const char * loid_arg);
|
||||
bool do_lo_list(PsqlSettings * pset);
|
||||
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_unlink(PsqlSettings *pset, const char *loid_arg);
|
||||
bool do_lo_list(PsqlSettings *pset);
|
||||
|
||||
#endif /* LARGE_OBJ_H */
|
||||
#endif /* LARGE_OBJ_H */
|
||||
|
|
|
@ -26,343 +26,380 @@
|
|||
int
|
||||
MainLoop(PsqlSettings *pset, FILE *source)
|
||||
{
|
||||
PQExpBuffer query_buf; /* buffer for query being accumulated */
|
||||
char *line; /* current line of input */
|
||||
char *xcomment; /* start of extended comment */
|
||||
int len; /* length of the line */
|
||||
int successResult = EXIT_SUCCESS;
|
||||
backslashResult slashCmdStatus;
|
||||
PQExpBuffer query_buf; /* buffer for query being accumulated */
|
||||
char *line; /* current line of input */
|
||||
char *xcomment; /* start of extended comment */
|
||||
int len; /* length of the line */
|
||||
int successResult = EXIT_SUCCESS;
|
||||
backslashResult slashCmdStatus;
|
||||
|
||||
bool eof = false; /* end of our command input? */
|
||||
bool success;
|
||||
char in_quote; /* == 0 for no in_quote */
|
||||
bool was_bslash; /* backslash */
|
||||
int paren_level;
|
||||
unsigned int query_start;
|
||||
bool eof = false; /* end of our command input? */
|
||||
bool success;
|
||||
char in_quote; /* == 0 for no in_quote */
|
||||
bool was_bslash; /* backslash */
|
||||
int paren_level;
|
||||
unsigned int query_start;
|
||||
|
||||
int i, prevlen, thislen;
|
||||
int i,
|
||||
prevlen,
|
||||
thislen;
|
||||
|
||||
/* Save the prior command source */
|
||||
FILE *prev_cmd_source;
|
||||
bool prev_cmd_interactive;
|
||||
/* Save the prior command source */
|
||||
FILE *prev_cmd_source;
|
||||
bool prev_cmd_interactive;
|
||||
|
||||
bool die_on_error;
|
||||
const char *interpol_char;
|
||||
bool die_on_error;
|
||||
const char *interpol_char;
|
||||
|
||||
|
||||
/* Save old settings */
|
||||
prev_cmd_source = pset->cur_cmd_source;
|
||||
prev_cmd_interactive = pset->cur_cmd_interactive;
|
||||
/* Save old settings */
|
||||
prev_cmd_source = pset->cur_cmd_source;
|
||||
prev_cmd_interactive = pset->cur_cmd_interactive;
|
||||
|
||||
/* Establish new source */
|
||||
pset->cur_cmd_source = source;
|
||||
pset->cur_cmd_interactive = ((source == stdin) && !pset->notty);
|
||||
/* Establish new source */
|
||||
pset->cur_cmd_source = source;
|
||||
pset->cur_cmd_interactive = ((source == stdin) && !pset->notty);
|
||||
|
||||
|
||||
query_buf = createPQExpBuffer();
|
||||
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)
|
||||
query_buf = createPQExpBuffer();
|
||||
if (!query_buf)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
perror("createPQExpBuffer");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
|
||||
xcomment = NULL;
|
||||
in_quote = 0;
|
||||
paren_level = 0;
|
||||
slashCmdStatus = CMD_UNKNOWN; /* set default */
|
||||
|
||||
|
||||
/* main loop to get queries and execute them */
|
||||
while (!eof)
|
||||
{
|
||||
/*
|
||||
* otherwise, set interactive prompt if necessary
|
||||
* and get another line
|
||||
*/
|
||||
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;
|
||||
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
|
||||
prompt_status = PROMPT_READY;
|
||||
{
|
||||
|
||||
line = gets_interactive(get_prompt(pset, prompt_status));
|
||||
}
|
||||
else
|
||||
line = gets_fromFile(source);
|
||||
}
|
||||
/*
|
||||
* otherwise, set interactive prompt if necessary and get
|
||||
* another line
|
||||
*/
|
||||
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 */
|
||||
die_on_error = GetVariableBool(pset->vars, "die_on_error");
|
||||
interpol_char = GetVariable(pset->vars, "sql_interpol");;
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
line = gets_interactive(get_prompt(pset, prompt_status));
|
||||
}
|
||||
else
|
||||
line = gets_fromFile(source);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* Setting these will not have effect until next line */
|
||||
die_on_error = GetVariableBool(pset->vars, "die_on_error");
|
||||
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);
|
||||
query_start = 0;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
/* 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);
|
||||
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))
|
||||
|
||||
success = true;
|
||||
for (i = 0; i < len; ADVANCE_1) {
|
||||
if (!success && die_on_error)
|
||||
break;
|
||||
success = true;
|
||||
for (i = 0; i < len; ADVANCE_1)
|
||||
{
|
||||
if (!success && die_on_error)
|
||||
break;
|
||||
|
||||
|
||||
/* was the previous character a backslash? */
|
||||
if (i > 0 && line[i - prevlen] == '\\')
|
||||
was_bslash = true;
|
||||
else
|
||||
was_bslash = false;
|
||||
|
||||
|
||||
/* in quote? */
|
||||
if (in_quote) {
|
||||
/* end of quote */
|
||||
if (line[i] == in_quote && !was_bslash)
|
||||
in_quote = '\0';
|
||||
}
|
||||
/* was the previous character a backslash? */
|
||||
if (i > 0 && line[i - prevlen] == '\\')
|
||||
was_bslash = true;
|
||||
else
|
||||
was_bslash = false;
|
||||
|
||||
/* start of quote */
|
||||
else if (line[i] == '\'' || line[i] == '"')
|
||||
in_quote = line[i];
|
||||
|
||||
/* in extended comment? */
|
||||
else if (xcomment != NULL) {
|
||||
if (line[i] == '*' && line[i + thislen] == '/') {
|
||||
xcomment = NULL;
|
||||
ADVANCE_1;
|
||||
}
|
||||
}
|
||||
|
||||
/* start of extended comment? */
|
||||
else if (line[i] == '/' && line[i + thislen] == '*') {
|
||||
xcomment = &line[i];
|
||||
ADVANCE_1;
|
||||
}
|
||||
/* in quote? */
|
||||
if (in_quote)
|
||||
{
|
||||
/* end of quote */
|
||||
if (line[i] == in_quote && !was_bslash)
|
||||
in_quote = '\0';
|
||||
}
|
||||
|
||||
/* single-line comment? truncate line */
|
||||
else if ((line[i] == '-' && line[i + thislen] == '-') ||
|
||||
(line[i] == '/' && line[i + thislen] == '/'))
|
||||
{
|
||||
line[i] = '\0'; /* remove comment */
|
||||
break;
|
||||
}
|
||||
/* start of quote */
|
||||
else if (line[i] == '\'' || line[i] == '"')
|
||||
in_quote = line[i];
|
||||
|
||||
/* count nested parentheses */
|
||||
else if (line[i] == '(')
|
||||
paren_level++;
|
||||
/* in extended comment? */
|
||||
else if (xcomment != NULL)
|
||||
{
|
||||
if (line[i] == '*' && line[i + thislen] == '/')
|
||||
{
|
||||
xcomment = NULL;
|
||||
ADVANCE_1;
|
||||
}
|
||||
}
|
||||
|
||||
else if (line[i] == ')' && paren_level > 0)
|
||||
paren_level--;
|
||||
/* start of extended comment? */
|
||||
else if (line[i] == '/' && line[i + thislen] == '*')
|
||||
{
|
||||
xcomment = &line[i];
|
||||
ADVANCE_1;
|
||||
}
|
||||
|
||||
/* semicolon? then send query */
|
||||
else if (line[i] == ';' && !was_bslash && paren_level==0) {
|
||||
line[i] = '\0';
|
||||
/* 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);
|
||||
/* single-line comment? truncate line */
|
||||
else if ((line[i] == '-' && line[i + thislen] == '-') ||
|
||||
(line[i] == '/' && line[i + thislen] == '/'))
|
||||
{
|
||||
line[i] = '\0'; /* remove comment */
|
||||
break;
|
||||
}
|
||||
|
||||
/* count nested parentheses */
|
||||
else if (line[i] == '(')
|
||||
paren_level++;
|
||||
|
||||
else if (line[i] == ')' && paren_level > 0)
|
||||
paren_level--;
|
||||
|
||||
/* semicolon? then send query */
|
||||
else if (line[i] == ';' && !was_bslash && paren_level == 0)
|
||||
{
|
||||
line[i] = '\0';
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
if (!success && die_on_error && !pset->cur_cmd_interactive)
|
||||
{
|
||||
successResult = EXIT_USER;
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
if (slashCmdStatus == CMD_TERMINATE)
|
||||
{
|
||||
successResult = EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
/* is there anything left after the backslash command? */
|
||||
if (end_of_cmd) {
|
||||
i += end_of_cmd - &line[i];
|
||||
query_start = i;
|
||||
|
||||
/* Put the rest of the line in the query buffer. */
|
||||
if (line[query_start + strspn(line + query_start, " \t")] != '\0')
|
||||
{
|
||||
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) {
|
||||
successResult = EXIT_USER;
|
||||
break;
|
||||
}
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
if (slashCmdStatus == CMD_TERMINATE) {
|
||||
successResult = EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
/* Have we lost the db connection? */
|
||||
if (pset->db == NULL && !pset->cur_cmd_interactive)
|
||||
{
|
||||
successResult = EXIT_BADCONN;
|
||||
break;
|
||||
}
|
||||
} /* while */
|
||||
|
||||
destroyPQExpBuffer(query_buf);
|
||||
|
||||
/* Put the rest of the line in the query buffer. */
|
||||
if (line[query_start + strspn(line + query_start, " \t")]!='\0') {
|
||||
if (query_buf->len > 0)
|
||||
appendPQExpBufferChar(query_buf, '\n');
|
||||
appendPQExpBufferStr(query_buf, line + query_start);
|
||||
}
|
||||
pset->cur_cmd_source = prev_cmd_source;
|
||||
pset->cur_cmd_interactive = prev_cmd_interactive;
|
||||
|
||||
free(line);
|
||||
|
||||
|
||||
/* 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;
|
||||
return successResult;
|
||||
} /* MainLoop() */
|
||||
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
#include "settings.h"
|
||||
|
||||
int
|
||||
MainLoop(PsqlSettings *pset, FILE *source);
|
||||
MainLoop(PsqlSettings *pset, FILE *source);
|
||||
|
||||
#endif MAINLOOP_H
|
||||
#endif /* MAINLOOP_H */
|
||||
|
|
1650
src/bin/psql/print.c
1650
src/bin/psql/print.c
File diff suppressed because it is too large
Load Diff
|
@ -7,52 +7,58 @@
|
|||
#include <stdio.h>
|
||||
#include <libpq-fe.h>
|
||||
|
||||
enum printFormat {
|
||||
PRINT_NOTHING = 0, /* to make sure someone initializes this */
|
||||
PRINT_UNALIGNED,
|
||||
PRINT_ALIGNED,
|
||||
PRINT_HTML,
|
||||
PRINT_LATEX
|
||||
/* add your favourite output format here ... */
|
||||
enum printFormat
|
||||
{
|
||||
PRINT_NOTHING = 0, /* to make sure someone initializes this */
|
||||
PRINT_UNALIGNED,
|
||||
PRINT_ALIGNED,
|
||||
PRINT_HTML,
|
||||
PRINT_LATEX
|
||||
/* add your favourite output format here ... */
|
||||
};
|
||||
|
||||
|
||||
typedef struct _printTableOpt {
|
||||
enum printFormat format; /* one of the above */
|
||||
bool expanded; /* expanded/vertical output (if supported by output format) */
|
||||
bool pager; /* use pager for output (if to stdout and stdout is a tty) */
|
||||
bool tuples_only; /* don't output headers, row counts, etc. */
|
||||
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;
|
||||
typedef struct _printTableOpt
|
||||
{
|
||||
enum printFormat format; /* one of the above */
|
||||
bool expanded; /* expanded/vertical output (if supported
|
||||
* by output format) */
|
||||
bool pager; /* use pager for output (if to stdout and
|
||||
* stdout is a tty) */
|
||||
bool tuples_only; /* don't output headers, row counts, etc. */
|
||||
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.
|
||||
* - title is just any string (NULL is fine)
|
||||
* - 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
|
||||
* column count is important
|
||||
* column count is important
|
||||
* - 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.
|
||||
* (You must specify this long enough. Otherwise anything could happen.)
|
||||
* (You must specify this long enough. Otherwise anything could happen.)
|
||||
*/
|
||||
void
|
||||
printTable(const char * title, char ** headers, char ** cells, char ** footers,
|
||||
const char * align,
|
||||
const printTableOpt * opt, FILE * fout);
|
||||
void printTable(const char *title, char **headers, char **cells, char **footers,
|
||||
const char *align,
|
||||
const printTableOpt * opt, FILE *fout);
|
||||
|
||||
|
||||
|
||||
typedef struct _printQueryOpt {
|
||||
printTableOpt topt; /* the options above */
|
||||
char * nullPrint; /* how to print null entities */
|
||||
bool quote; /* quote all values as much as possible */
|
||||
char * title; /* override title */
|
||||
char ** footers; /* override footer (default is "(xx rows)") */
|
||||
} printQueryOpt;
|
||||
typedef struct _printQueryOpt
|
||||
{
|
||||
printTableOpt topt; /* the options above */
|
||||
char *nullPrint; /* how to print null entities */
|
||||
bool quote; /* quote all values as much as possible */
|
||||
char *title; /* override title */
|
||||
char **footers; /* override footer (default is "(xx
|
||||
* rows)") */
|
||||
} printQueryOpt;
|
||||
|
||||
/*
|
||||
* Use this to print query results
|
||||
|
@ -60,7 +66,7 @@ typedef struct _printQueryOpt {
|
|||
* It calls the printTable above with all the things set straight.
|
||||
*/
|
||||
void
|
||||
printQuery(PGresult * result, const printQueryOpt * opt, FILE * fout);
|
||||
printQuery(PGresult *result, const printQueryOpt * opt, FILE *fout);
|
||||
|
||||
|
||||
#endif /* PRINT_H */
|
||||
#endif /* PRINT_H */
|
||||
|
|
|
@ -34,20 +34,20 @@
|
|||
* %~ - like %/ but "~" when database name equals user name
|
||||
* %# - "#" if the username is postgres, ">" otherwise
|
||||
* %R - in prompt1 normally =, or ^ if single line mode,
|
||||
* or a ! if session is not connected to a database;
|
||||
* in prompt2 -, *, ', or ";
|
||||
* in prompt3 nothing
|
||||
* or a ! if session is not connected to a database;
|
||||
* in prompt2 -, *, ', or ";
|
||||
* in prompt3 nothing
|
||||
* %? - the error code of the last query (not yet implemented)
|
||||
* %% - a percent sign
|
||||
*
|
||||
* %[0-9] - the character with the given decimal code
|
||||
* %0[0-7] - the character with the given octal code
|
||||
* %[0-9] - the character with the given decimal code
|
||||
* %0[0-7] - the character with the given octal code
|
||||
* %0x[0-9A-Fa-f] - the character with the given hexadecimal code
|
||||
*
|
||||
* %`command` - The result of executing command in /bin/sh with trailing
|
||||
* newline stripped.
|
||||
* %$name$ - The value of the psql/environment/magic varible 'name'
|
||||
* (same rules as for, e.g., \echo $foo)
|
||||
* %`command` - The result of executing command in /bin/sh with trailing
|
||||
* newline stripped.
|
||||
* %$name$ - The value of the psql/environment/magic varible 'name'
|
||||
* (same rules as for, e.g., \echo $foo)
|
||||
* (those will not be rescanned for more escape sequences!)
|
||||
*
|
||||
*
|
||||
|
@ -60,197 +60,214 @@ const char *
|
|||
get_prompt(PsqlSettings *pset, promptStatus_t status)
|
||||
{
|
||||
#define MAX_PROMPT_SIZE 256
|
||||
static char destination[MAX_PROMPT_SIZE+1];
|
||||
char buf[MAX_PROMPT_SIZE+1];
|
||||
bool esc = false;
|
||||
const char *p;
|
||||
const char * prompt_string;
|
||||
static char destination[MAX_PROMPT_SIZE + 1];
|
||||
char buf[MAX_PROMPT_SIZE + 1];
|
||||
bool esc = false;
|
||||
const char *p;
|
||||
const char *prompt_string;
|
||||
|
||||
if (GetVariable(pset->vars, "quiet"))
|
||||
return "";
|
||||
if (GetVariable(pset->vars, "quiet"))
|
||||
return "";
|
||||
|
||||
if (status == PROMPT_READY)
|
||||
prompt_string = GetVariable(pset->vars, "prompt1");
|
||||
else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT)
|
||||
prompt_string = GetVariable(pset->vars, "prompt2");
|
||||
else if (status == PROMPT_COPY)
|
||||
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;
|
||||
if (status == PROMPT_READY)
|
||||
prompt_string = GetVariable(pset->vars, "prompt1");
|
||||
else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT)
|
||||
prompt_string = GetVariable(pset->vars, "prompt2");
|
||||
else if (status == PROMPT_COPY)
|
||||
prompt_string = GetVariable(pset->vars, "prompt3");
|
||||
else
|
||||
prompt_string = "? ";
|
||||
|
||||
|
||||
destination[0] = '\0';
|
||||
|
||||
for (p = prompt_string;
|
||||
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
|
||||
p++)
|
||||
{
|
||||
buf[0] = *p;
|
||||
buf[1] = '\0';
|
||||
esc = false;
|
||||
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
|
||||
{
|
||||
buf[0] = *p;
|
||||
buf[1] = '\0';
|
||||
esc = false;
|
||||
}
|
||||
|
||||
if (!esc)
|
||||
strncat(destination, buf, MAX_PROMPT_SIZE - strlen(destination));
|
||||
}
|
||||
|
||||
if (!esc) {
|
||||
strncat(destination, buf, MAX_PROMPT_SIZE-strlen(destination));
|
||||
}
|
||||
}
|
||||
|
||||
destination[MAX_PROMPT_SIZE] = '\0';
|
||||
return destination;
|
||||
destination[MAX_PROMPT_SIZE] = '\0';
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,17 +3,18 @@
|
|||
|
||||
#include "settings.h"
|
||||
|
||||
typedef enum _promptStatus {
|
||||
PROMPT_READY,
|
||||
PROMPT_CONTINUE,
|
||||
PROMPT_COMMENT,
|
||||
PROMPT_SINGLEQUOTE,
|
||||
PROMPT_DOUBLEQUOTE,
|
||||
PROMPT_COPY
|
||||
} promptStatus_t;
|
||||
typedef enum _promptStatus
|
||||
{
|
||||
PROMPT_READY,
|
||||
PROMPT_CONTINUE,
|
||||
PROMPT_COMMENT,
|
||||
PROMPT_SINGLEQUOTE,
|
||||
PROMPT_DOUBLEQUOTE,
|
||||
PROMPT_COPY
|
||||
} promptStatus_t;
|
||||
|
||||
const char *
|
||||
get_prompt(PsqlSettings *pset, promptStatus_t status);
|
||||
get_prompt(PsqlSettings *pset, promptStatus_t status);
|
||||
|
||||
|
||||
#endif /* PROMPT_H */
|
||||
#endif /* PROMPT_H */
|
||||
|
|
3297
src/bin/psql/psql.c
3297
src/bin/psql/psql.c
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* 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\
|
||||
\tor\n\
|
||||
\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! */
|
||||
};
|
||||
|
|
|
@ -20,24 +20,27 @@
|
|||
|
||||
typedef struct _psqlSettings
|
||||
{
|
||||
PGconn *db; /* connection to backend */
|
||||
FILE *queryFout; /* where to send the query results */
|
||||
bool queryFoutPipe; /* queryFout is from a popen() */
|
||||
PGconn *db; /* connection to backend */
|
||||
FILE *queryFout; /* where to send the query results */
|
||||
bool queryFoutPipe; /* queryFout is from a popen() */
|
||||
|
||||
printQueryOpt popt;
|
||||
VariableSpace vars; /* "shell variable" repository */
|
||||
printQueryOpt popt;
|
||||
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 useReadline; /* use libreadline routines */
|
||||
bool useHistory;
|
||||
bool getPassword; /* prompt the user for a username and
|
||||
password */
|
||||
FILE * cur_cmd_source; /* describe the status of the current main loop */
|
||||
bool cur_cmd_interactive;
|
||||
bool notty; /* stdin or stdout is not a tty (as
|
||||
* determined on startup) */
|
||||
bool useReadline; /* use libreadline routines */
|
||||
bool useHistory;
|
||||
bool getPassword; /* prompt the user for a username and
|
||||
* password */
|
||||
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;
|
||||
|
||||
|
||||
|
|
|
@ -7,251 +7,251 @@
|
|||
|
||||
struct _helpStruct
|
||||
{
|
||||
char *cmd; /* the command name */
|
||||
char *help; /* the help associated with it */
|
||||
char *syntax; /* the syntax associated with it */
|
||||
char *cmd; /* the command name */
|
||||
char *help; /* the help associated with it */
|
||||
char *syntax; /* the syntax associated with it */
|
||||
};
|
||||
|
||||
|
||||
static struct _helpStruct QL_HELP[] = {
|
||||
{ "TRUNCATE",
|
||||
"Empty a table",
|
||||
"TRUNCATE [ TABLE ] name" },
|
||||
{"TRUNCATE",
|
||||
"Empty a table",
|
||||
"TRUNCATE [ TABLE ] name"},
|
||||
|
||||
{ "ABORT",
|
||||
"Aborts the current transaction",
|
||||
"ABORT [ WORK | TRANSACTION ]" },
|
||||
{"ABORT",
|
||||
"Aborts the current transaction",
|
||||
"ABORT [ WORK | TRANSACTION ]"},
|
||||
|
||||
{ "ALTER TABLE",
|
||||
"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",
|
||||
"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 USER",
|
||||
"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",
|
||||
"Modifies user account information",
|
||||
"ALTER USER username [ WITH PASSWORD password ]\n [ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]\n [ IN GROUP groupname [, ...] ]\n [ VALID UNTIL 'abstime' ]"},
|
||||
|
||||
{ "BEGIN",
|
||||
"Begins a transaction in chained mode",
|
||||
"BEGIN [ WORK | TRANSACTION ]" },
|
||||
{"BEGIN",
|
||||
"Begins a transaction in chained mode",
|
||||
"BEGIN [ WORK | TRANSACTION ]"},
|
||||
|
||||
{ "CLOSE",
|
||||
"Close a cursor",
|
||||
"CLOSE cursor" },
|
||||
{"CLOSE",
|
||||
"Close a cursor",
|
||||
"CLOSE cursor"},
|
||||
|
||||
{ "CLUSTER",
|
||||
"Gives storage clustering advice to the server",
|
||||
"CLUSTER indexname ON table" },
|
||||
{"CLUSTER",
|
||||
"Gives storage clustering advice to the server",
|
||||
"CLUSTER indexname ON table"},
|
||||
|
||||
{ "COMMIT",
|
||||
"Commits the current transaction",
|
||||
"COMMIT [ WORK | TRANSACTION ]" },
|
||||
{"COMMIT",
|
||||
"Commits the current transaction",
|
||||
"COMMIT [ WORK | TRANSACTION ]"},
|
||||
|
||||
{ "COPY",
|
||||
"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",
|
||||
"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' ]"},
|
||||
|
||||
{ "CREATE AGGREGATE",
|
||||
"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",
|
||||
"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 DATABASE",
|
||||
"Creates a new database",
|
||||
"CREATE DATABASE name [ WITH LOCATION = 'dbpath' ]" },
|
||||
{"CREATE DATABASE",
|
||||
"Creates a new database",
|
||||
"CREATE DATABASE name [ WITH LOCATION = 'dbpath' ]"},
|
||||
|
||||
{ "CREATE 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",
|
||||
"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 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 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 LANGUAGE",
|
||||
"Defines a new language for functions",
|
||||
"CREATE [ TRUSTED ] PROCEDURAL LANGUAGE 'langname'\n HANDLER call_handler\n LANCOMPILER 'comment'" },
|
||||
{"CREATE LANGUAGE",
|
||||
"Defines a new language for functions",
|
||||
"CREATE [ TRUSTED ] PROCEDURAL LANGUAGE 'langname'\n HANDLER call_handler\n LANCOMPILER 'comment'"},
|
||||
|
||||
{ "CREATE 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",
|
||||
"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 RULE",
|
||||
"Defines a new rule",
|
||||
"CREATE RULE name AS ON event\n TO object [ WHERE condition ]\n DO [ INSTEAD ] [ action | NOTHING ]" },
|
||||
{"CREATE RULE",
|
||||
"Defines a new rule",
|
||||
"CREATE RULE name AS ON event\n TO object [ WHERE condition ]\n DO [ INSTEAD ] [ action | NOTHING ]"},
|
||||
|
||||
{ "CREATE SEQUENCE",
|
||||
"Creates a new sequence number generator",
|
||||
"CREATE SEQUENCE seqname [ INCREMENT increment ]\n [ MINVALUE minvalue ] [ MAXVALUE maxvalue ]\n [ START start ] [ CACHE cache ] [ CYCLE ]" },
|
||||
{"CREATE SEQUENCE",
|
||||
"Creates a new sequence number generator",
|
||||
"CREATE SEQUENCE seqname [ INCREMENT increment ]\n [ MINVALUE minvalue ] [ MAXVALUE maxvalue ]\n [ START start ] [ CACHE cache ] [ CYCLE ]"},
|
||||
|
||||
{ "CREATE 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 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 TABLE AS",
|
||||
"Creates a new table",
|
||||
"CREATE TABLE table [ (column [, ...] ) ]\n AS select_clause" },
|
||||
{"CREATE TABLE AS",
|
||||
"Creates a new table",
|
||||
"CREATE TABLE table [ (column [, ...] ) ]\n AS select_clause"},
|
||||
|
||||
{ "CREATE 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",
|
||||
"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 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",
|
||||
"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 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",
|
||||
"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 VIEW",
|
||||
"Constructs a virtual table",
|
||||
"CREATE VIEW view AS SELECT query" },
|
||||
{"CREATE VIEW",
|
||||
"Constructs a virtual table",
|
||||
"CREATE VIEW view AS SELECT query"},
|
||||
|
||||
{ "DECLARE",
|
||||
"Defines a cursor for table access",
|
||||
"DECLARE cursor [ BINARY ] [ INSENSITIVE ] [ SCROLL ]\n CURSOR FOR query\n [ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]" },
|
||||
{"DECLARE",
|
||||
"Defines a cursor for table access",
|
||||
"DECLARE cursor [ BINARY ] [ INSENSITIVE ] [ SCROLL ]\n CURSOR FOR query\n [ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]"},
|
||||
|
||||
{ "DELETE",
|
||||
"Removes rows from a table",
|
||||
"DELETE FROM table [ WHERE condition ]" },
|
||||
{"DELETE",
|
||||
"Removes rows from a table",
|
||||
"DELETE FROM table [ WHERE condition ]"},
|
||||
|
||||
{ "DROP AGGREGATE",
|
||||
"Removes the definition of an aggregate function",
|
||||
"DROP AGGREGATE name type" },
|
||||
{"DROP AGGREGATE",
|
||||
"Removes the definition of an aggregate function",
|
||||
"DROP AGGREGATE name type"},
|
||||
|
||||
{ "FETCH",
|
||||
"Gets rows using a cursor",
|
||||
"FETCH [ selector ] [ count ] { IN | FROM } cursor\nFETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor" },
|
||||
{"FETCH",
|
||||
"Gets rows using a cursor",
|
||||
"FETCH [ selector ] [ count ] { IN | FROM } cursor\nFETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"},
|
||||
|
||||
{ "DROP DATABASE",
|
||||
"Destroys an existing database",
|
||||
"DROP DATABASE name" },
|
||||
{"DROP DATABASE",
|
||||
"Destroys an existing database",
|
||||
"DROP DATABASE name"},
|
||||
|
||||
{ "DROP FUNCTION",
|
||||
"Removes a user-defined C function",
|
||||
"DROP FUNCTION name ( [ type [, ...] ] )" },
|
||||
{"DROP FUNCTION",
|
||||
"Removes a user-defined C function",
|
||||
"DROP FUNCTION name ( [ type [, ...] ] )"},
|
||||
|
||||
{ "DROP INDEX",
|
||||
"Removes an index from a database",
|
||||
"DROP INDEX index_name" },
|
||||
{"DROP INDEX",
|
||||
"Removes an index from a database",
|
||||
"DROP INDEX index_name"},
|
||||
|
||||
{ "DROP LANGUAGE",
|
||||
"Removes a user-defined procedural language",
|
||||
"DROP PROCEDURAL LANGUAGE 'name'" },
|
||||
{"DROP LANGUAGE",
|
||||
"Removes a user-defined procedural language",
|
||||
"DROP PROCEDURAL LANGUAGE 'name'"},
|
||||
|
||||
{ "DROP OPERATOR",
|
||||
"Removes an operator from the database",
|
||||
"DROP OPERATOR id ( type | NONE [,...] )" },
|
||||
{"DROP OPERATOR",
|
||||
"Removes an operator from the database",
|
||||
"DROP OPERATOR id ( type | NONE [,...] )"},
|
||||
|
||||
{ "DROP RULE",
|
||||
"Removes an existing rule from the database",
|
||||
"DROP RULE name" },
|
||||
{"DROP RULE",
|
||||
"Removes an existing rule from the database",
|
||||
"DROP RULE name"},
|
||||
|
||||
{ "DROP SEQUENCE",
|
||||
"Removes an existing sequence",
|
||||
"DROP SEQUENCE name [, ...]" },
|
||||
{"DROP SEQUENCE",
|
||||
"Removes an existing sequence",
|
||||
"DROP SEQUENCE name [, ...]"},
|
||||
|
||||
{ "DROP TABLE",
|
||||
"Removes existing tables from a database",
|
||||
"DROP TABLE name [, ...]" },
|
||||
{"DROP TABLE",
|
||||
"Removes existing tables from a database",
|
||||
"DROP TABLE name [, ...]"},
|
||||
|
||||
{ "DROP TRIGGER",
|
||||
"Removes the definition of a trigger",
|
||||
"DROP TRIGGER name ON table" },
|
||||
{"DROP TRIGGER",
|
||||
"Removes the definition of a trigger",
|
||||
"DROP TRIGGER name ON table"},
|
||||
|
||||
{ "DROP TYPE",
|
||||
"Removes a user-defined type from the system catalogs",
|
||||
"DROP TYPE typename" },
|
||||
{"DROP TYPE",
|
||||
"Removes a user-defined type from the system catalogs",
|
||||
"DROP TYPE typename"},
|
||||
|
||||
{ "DROP USER",
|
||||
"Removes an user account information",
|
||||
"DROP USER name" },
|
||||
{"DROP USER",
|
||||
"Removes an user account information",
|
||||
"DROP USER name"},
|
||||
|
||||
{ "DROP VIEW",
|
||||
"Removes an existing view from a database",
|
||||
"DROP VIEW name" },
|
||||
{"DROP VIEW",
|
||||
"Removes an existing view from a database",
|
||||
"DROP VIEW name"},
|
||||
|
||||
{ "EXPLAIN",
|
||||
"Shows statement execution details",
|
||||
"EXPLAIN [ VERBOSE ] query" },
|
||||
{"EXPLAIN",
|
||||
"Shows statement execution details",
|
||||
"EXPLAIN [ VERBOSE ] query"},
|
||||
|
||||
{ "GRANT",
|
||||
"Grants access privilege to a user, a group or all users",
|
||||
"GRANT privilege [, ...] ON object [, ...]\n TO { PUBLIC | GROUP group | username }" },
|
||||
{"GRANT",
|
||||
"Grants access privilege to a user, a group or all users",
|
||||
"GRANT privilege [, ...] ON object [, ...]\n TO { PUBLIC | GROUP group | username }"},
|
||||
|
||||
{ "INSERT",
|
||||
"Inserts new rows into a table",
|
||||
"INSERT INTO table [ ( column [, ...] ) ]\n { VALUES ( expression [, ...] ) | SELECT query }" },
|
||||
{"INSERT",
|
||||
"Inserts new rows into a table",
|
||||
"INSERT INTO table [ ( column [, ...] ) ]\n { VALUES ( expression [, ...] ) | SELECT query }"},
|
||||
|
||||
{ "LISTEN",
|
||||
"Listen for a response on a notify condition",
|
||||
"LISTEN name" },
|
||||
{"LISTEN",
|
||||
"Listen for a response on a notify condition",
|
||||
"LISTEN name"},
|
||||
|
||||
{ "LOAD",
|
||||
"Dynamically loads an object file",
|
||||
"LOAD 'filename'" },
|
||||
{"LOAD",
|
||||
"Dynamically loads an object file",
|
||||
"LOAD 'filename'"},
|
||||
|
||||
{ "LOCK",
|
||||
"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",
|
||||
"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"},
|
||||
|
||||
{ "MOVE",
|
||||
"Moves cursor position",
|
||||
"MOVE [ selector ] [ count ] \n { IN | FROM } cursor\n FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor" },
|
||||
{"MOVE",
|
||||
"Moves cursor position",
|
||||
"MOVE [ selector ] [ count ] \n { IN | FROM } cursor\n FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"},
|
||||
|
||||
{ "NOTIFY",
|
||||
"Signals all frontends and backends listening on a notify condition",
|
||||
"NOTIFY name" },
|
||||
{"NOTIFY",
|
||||
"Signals all frontends and backends listening on a notify condition",
|
||||
"NOTIFY name"},
|
||||
|
||||
{ "RESET",
|
||||
"Restores run-time parameters for session to default values",
|
||||
"RESET variable" },
|
||||
{"RESET",
|
||||
"Restores run-time parameters for session to default values",
|
||||
"RESET variable"},
|
||||
|
||||
{ "REVOKE",
|
||||
"Revokes access privilege from a user, a group or all users.",
|
||||
"REVOKE privilege [, ...]\n ON object [, ...]\n FROM { PUBLIC | GROUP ER\">gBLE> | username }" },
|
||||
{"REVOKE",
|
||||
"Revokes access privilege from a user, a group or all users.",
|
||||
"REVOKE privilege [, ...]\n ON object [, ...]\n FROM { PUBLIC | GROUP ER\">gBLE> | username }"},
|
||||
|
||||
{ "ROLLBACK",
|
||||
"Aborts the current transaction",
|
||||
"ROLLBACK [ WORK | TRANSACTION ]" },
|
||||
{"ROLLBACK",
|
||||
"Aborts the current transaction",
|
||||
"ROLLBACK [ WORK | TRANSACTION ]"},
|
||||
|
||||
{ "SELECT",
|
||||
"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",
|
||||
"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 INTO",
|
||||
"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 INTO",
|
||||
"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]]"},
|
||||
|
||||
{ "SET",
|
||||
"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",
|
||||
"Set run-time parameters for session",
|
||||
"SET variable { TO | = } { 'value' | DEFAULT }\nSET TIME ZONE { 'timezone' | LOCAL | DEFAULT }\nSET TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE }"},
|
||||
|
||||
{ "SHOW",
|
||||
"Shows run-time parameters for session",
|
||||
"SHOW keyword" },
|
||||
{"SHOW",
|
||||
"Shows run-time parameters for session",
|
||||
"SHOW keyword"},
|
||||
|
||||
{ "UNLISTEN",
|
||||
"Stop listening for notification",
|
||||
"UNLISTEN { notifyname | * }" },
|
||||
{"UNLISTEN",
|
||||
"Stop listening for notification",
|
||||
"UNLISTEN { notifyname | * }"},
|
||||
|
||||
{ "UPDATE",
|
||||
"Replaces values of columns in a table",
|
||||
"UPDATE table SET R\">colle> = expression [, ...]\n [ FROM fromlist ]\n [ WHERE condition ]" },
|
||||
{"UPDATE",
|
||||
"Replaces values of columns in a table",
|
||||
"UPDATE table SET R\">colle> = expression [, ...]\n [ FROM fromlist ]\n [ WHERE condition ]"},
|
||||
|
||||
{ "VACUUM",
|
||||
"Clean and analyze a Postgres database",
|
||||
"VACUUM [ VERBOSE ] [ ANALYZE ] [ table ]\nVACUUM [ VERBOSE ] ANALYZE [ ER\">tBLE> [ (column [, ...] ) ] ]" },
|
||||
{"VACUUM",
|
||||
"Clean and analyze a Postgres database",
|
||||
"VACUUM [ VERBOSE ] [ ANALYZE ] [ table ]\nVACUUM [ VERBOSE ] ANALYZE [ ER\">tBLE> [ (column [, ...] ) ] ]"},
|
||||
|
||||
{ "END",
|
||||
"Commits the current transaction",
|
||||
"END [ WORK | TRANSACTION ]" },
|
||||
{"END",
|
||||
"Commits the current transaction",
|
||||
"END [ WORK | TRANSACTION ]"},
|
||||
|
||||
{ "COMMENT",
|
||||
"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",
|
||||
"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'"},
|
||||
|
||||
|
||||
{ NULL, NULL, NULL } /* End of list marker */
|
||||
{NULL, NULL, NULL} /* End of list marker */
|
||||
};
|
||||
|
||||
#endif /* SQL_HELP_H */
|
||||
#endif /* SQL_HELP_H */
|
||||
|
|
|
@ -37,35 +37,38 @@
|
|||
|
||||
|
||||
static void
|
||||
process_psqlrc(PsqlSettings * pset);
|
||||
process_psqlrc(PsqlSettings *pset);
|
||||
|
||||
static void
|
||||
showVersion(PsqlSettings *pset, bool verbose);
|
||||
showVersion(PsqlSettings *pset, bool verbose);
|
||||
|
||||
|
||||
/* Structures to pass information between the option parsing routine
|
||||
* and the main function
|
||||
*/
|
||||
enum _actions { ACT_NOTHING = 0,
|
||||
ACT_SINGLE_SLASH,
|
||||
ACT_LIST_DB,
|
||||
ACT_SHOW_VER,
|
||||
ACT_SINGLE_QUERY,
|
||||
ACT_FILE
|
||||
enum _actions
|
||||
{
|
||||
ACT_NOTHING = 0,
|
||||
ACT_SINGLE_SLASH,
|
||||
ACT_LIST_DB,
|
||||
ACT_SHOW_VER,
|
||||
ACT_SINGLE_QUERY,
|
||||
ACT_FILE
|
||||
};
|
||||
|
||||
struct adhoc_opts {
|
||||
char * dbname;
|
||||
char * host;
|
||||
char * port;
|
||||
char * username;
|
||||
enum _actions action;
|
||||
char * action_string;
|
||||
bool no_readline;
|
||||
struct adhoc_opts
|
||||
{
|
||||
char *dbname;
|
||||
char *host;
|
||||
char *port;
|
||||
char *username;
|
||||
enum _actions action;
|
||||
char *action_string;
|
||||
bool no_readline;
|
||||
};
|
||||
|
||||
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
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
PsqlSettings settings;
|
||||
struct adhoc_opts options;
|
||||
int successResult;
|
||||
PsqlSettings settings;
|
||||
struct adhoc_opts options;
|
||||
int successResult;
|
||||
|
||||
char * username = NULL;
|
||||
char * password = NULL;
|
||||
bool need_pass;
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
bool need_pass;
|
||||
|
||||
MemSet(&settings, 0, sizeof settings);
|
||||
MemSet(&settings, 0, sizeof settings);
|
||||
|
||||
settings.cur_cmd_source = stdin;
|
||||
settings.cur_cmd_interactive = false;
|
||||
settings.cur_cmd_source = stdin;
|
||||
settings.cur_cmd_interactive = false;
|
||||
|
||||
settings.vars = CreateVariableSpace();
|
||||
settings.popt.topt.format = PRINT_ALIGNED;
|
||||
settings.queryFout = stdout;
|
||||
settings.popt.topt.fieldSep = strdup(DEFAULT_FIELD_SEP);
|
||||
settings.popt.topt.border = 1;
|
||||
settings.vars = CreateVariableSpace();
|
||||
settings.popt.topt.format = PRINT_ALIGNED;
|
||||
settings.queryFout = stdout;
|
||||
settings.popt.topt.fieldSep = strdup(DEFAULT_FIELD_SEP);
|
||||
settings.popt.topt.border = 1;
|
||||
|
||||
SetVariable(settings.vars, "prompt1", DEFAULT_PROMPT1);
|
||||
SetVariable(settings.vars, "prompt2", DEFAULT_PROMPT2);
|
||||
SetVariable(settings.vars, "prompt3", DEFAULT_PROMPT3);
|
||||
SetVariable(settings.vars, "prompt1", DEFAULT_PROMPT1);
|
||||
SetVariable(settings.vars, "prompt2", DEFAULT_PROMPT2);
|
||||
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
|
||||
settings.getPassword = true;
|
||||
settings.getPassword = true;
|
||||
#else
|
||||
settings.getPassword = false;
|
||||
settings.getPassword = false;
|
||||
#endif
|
||||
|
||||
#ifdef MULTIBYTE
|
||||
settings.has_client_encoding = (getenv("PGCLIENTENCODING") != NULL);
|
||||
settings.has_client_encoding = (getenv("PGCLIENTENCODING") != NULL);
|
||||
#endif
|
||||
|
||||
parse_options(argc, argv, &settings, &options);
|
||||
parse_options(argc, argv, &settings, &options);
|
||||
|
||||
if (options.action==ACT_LIST_DB || options.action==ACT_SHOW_VER)
|
||||
options.dbname = "template1";
|
||||
if (options.action == ACT_LIST_DB || options.action == ACT_SHOW_VER)
|
||||
options.dbname = "template1";
|
||||
|
||||
if (options.username) {
|
||||
if (strcmp(options.username, "?")==0)
|
||||
username = simple_prompt("Username: ", 100, true);
|
||||
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);
|
||||
if (options.username)
|
||||
{
|
||||
if (strcmp(options.username, "?") == 0)
|
||||
username = simple_prompt("Username: ", 100, true);
|
||||
else
|
||||
username = strdup(options.username);
|
||||
}
|
||||
} while (need_pass);
|
||||
|
||||
free(username);
|
||||
free(password);
|
||||
if (settings.getPassword)
|
||||
password = simple_prompt("Password: ", 100, false);
|
||||
|
||||
if (PQstatus(settings.db) == CONNECTION_BAD) {
|
||||
fprintf(stderr, "Connection to database '%s' failed.\n%s\n", PQdb(settings.db), PQerrorMessage(settings.db));
|
||||
/* 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);
|
||||
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);
|
||||
exit(EXIT_BADCONN);
|
||||
}
|
||||
setQFout(NULL, &settings);
|
||||
DestroyVariableSpace(settings.vars);
|
||||
|
||||
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);
|
||||
setQFout(NULL, &settings);
|
||||
DestroyVariableSpace(settings.vars);
|
||||
|
||||
return successResult;
|
||||
return successResult;
|
||||
}
|
||||
|
||||
|
||||
|
@ -214,215 +224,231 @@ main(int argc, char **argv)
|
|||
|
||||
#ifdef 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
|
||||
|
||||
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
|
||||
static struct option long_options[] = {
|
||||
{ "no-align", no_argument, NULL, 'A' },
|
||||
{ "command", required_argument, NULL, 'c' },
|
||||
{ "database", required_argument, NULL, 'd' },
|
||||
{ "dbname", required_argument, NULL, 'd' },
|
||||
{ "echo", no_argument, NULL, 'e' },
|
||||
{ "echo-queries", no_argument, NULL, 'e' },
|
||||
{ "echo-all", no_argument, NULL, 'E' },
|
||||
{ "echo-all-queries", no_argument, NULL, 'E' },
|
||||
{ "file", required_argument, NULL, 'f' },
|
||||
{ "field-sep", required_argument, NULL, 'F' },
|
||||
{ "host", required_argument, NULL, 'h' },
|
||||
{ "html", no_argument, NULL, 'H' },
|
||||
{ "list", no_argument, NULL, 'l' },
|
||||
{ "no-readline", no_argument, NULL, 'n' },
|
||||
{ "out", required_argument, NULL, 'o' },
|
||||
{ "to-file", required_argument, NULL, 'o' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "pset", required_argument, NULL, 'P' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "single-step", no_argument, NULL, 's' },
|
||||
{ "single-line", no_argument, NULL, 'S' },
|
||||
{ "tuples-only", no_argument, NULL, 't' },
|
||||
{ "table-attr", required_argument, NULL, 'T' },
|
||||
{ "username", required_argument, NULL, 'U' },
|
||||
{ "expanded", no_argument, NULL, 'x' },
|
||||
{ "set", required_argument, NULL, 'v' },
|
||||
{ "variable", required_argument, NULL, 'v' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "password", no_argument, NULL, 'W' },
|
||||
{ "help", no_argument, NULL, '?' },
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"no-align", no_argument, NULL, 'A'},
|
||||
{"command", required_argument, NULL, 'c'},
|
||||
{"database", required_argument, NULL, 'd'},
|
||||
{"dbname", required_argument, NULL, 'd'},
|
||||
{"echo", no_argument, NULL, 'e'},
|
||||
{"echo-queries", no_argument, NULL, 'e'},
|
||||
{"echo-all", no_argument, NULL, 'E'},
|
||||
{"echo-all-queries", no_argument, NULL, 'E'},
|
||||
{"file", required_argument, NULL, 'f'},
|
||||
{"field-sep", required_argument, NULL, 'F'},
|
||||
{"host", required_argument, NULL, 'h'},
|
||||
{"html", no_argument, NULL, 'H'},
|
||||
{"list", no_argument, NULL, 'l'},
|
||||
{"no-readline", no_argument, NULL, 'n'},
|
||||
{"out", required_argument, NULL, 'o'},
|
||||
{"to-file", required_argument, NULL, 'o'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"pset", required_argument, NULL, 'P'},
|
||||
{"quiet", no_argument, NULL, 'q'},
|
||||
{"single-step", no_argument, NULL, 's'},
|
||||
{"single-line", no_argument, NULL, 'S'},
|
||||
{"tuples-only", no_argument, NULL, 't'},
|
||||
{"table-attr", required_argument, NULL, 'T'},
|
||||
{"username", required_argument, NULL, 'U'},
|
||||
{"expanded", no_argument, NULL, 'x'},
|
||||
{"set", required_argument, NULL, 'v'},
|
||||
{"variable", required_argument, NULL, 'v'},
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{"password", no_argument, NULL, 'W'},
|
||||
{"help", no_argument, NULL, '?'},
|
||||
};
|
||||
|
||||
int optindex;
|
||||
|
||||
int optindex;
|
||||
#endif
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
int c;
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
int c;
|
||||
|
||||
MemSet(options, 0, sizeof *options);
|
||||
MemSet(options, 0, sizeof *options);
|
||||
|
||||
#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
|
||||
/* 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
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'A':
|
||||
pset->popt.topt.format = PRINT_UNALIGNED;
|
||||
break;
|
||||
case 'c':
|
||||
options->action_string = optarg;
|
||||
if (optarg[0] == '\\')
|
||||
options->action = ACT_SINGLE_SLASH;
|
||||
else
|
||||
options->action = ACT_SINGLE_QUERY;
|
||||
break;
|
||||
case 'd':
|
||||
options->dbname = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
SetVariable(pset->vars, "echo", "");
|
||||
break;
|
||||
case 'E':
|
||||
SetVariable(pset->vars, "echo_secret", "");
|
||||
break;
|
||||
case 'f':
|
||||
options->action = ACT_FILE;
|
||||
options->action_string = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
pset->popt.topt.fieldSep = strdup(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
options->host = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
pset->popt.topt.format = PRINT_HTML;
|
||||
break;
|
||||
case 'l':
|
||||
options->action = ACT_LIST_DB;
|
||||
break;
|
||||
case 'n':
|
||||
options->no_readline = true;
|
||||
break;
|
||||
case 'o':
|
||||
setQFout(optarg, pset);
|
||||
break;
|
||||
case 'p':
|
||||
options->port = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
{
|
||||
char *value;
|
||||
char *equal_loc;
|
||||
bool result;
|
||||
switch (c)
|
||||
{
|
||||
case 'A':
|
||||
pset->popt.topt.format = PRINT_UNALIGNED;
|
||||
break;
|
||||
case 'c':
|
||||
options->action_string = optarg;
|
||||
if (optarg[0] == '\\')
|
||||
options->action = ACT_SINGLE_SLASH;
|
||||
else
|
||||
options->action = ACT_SINGLE_QUERY;
|
||||
break;
|
||||
case 'd':
|
||||
options->dbname = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
SetVariable(pset->vars, "echo", "");
|
||||
break;
|
||||
case 'E':
|
||||
SetVariable(pset->vars, "echo_secret", "");
|
||||
break;
|
||||
case 'f':
|
||||
options->action = ACT_FILE;
|
||||
options->action_string = optarg;
|
||||
break;
|
||||
case 'F':
|
||||
pset->popt.topt.fieldSep = strdup(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
options->host = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
pset->popt.topt.format = PRINT_HTML;
|
||||
break;
|
||||
case 'l':
|
||||
options->action = ACT_LIST_DB;
|
||||
break;
|
||||
case 'n':
|
||||
options->no_readline = true;
|
||||
break;
|
||||
case 'o':
|
||||
setQFout(optarg, pset);
|
||||
break;
|
||||
case 'p':
|
||||
options->port = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
{
|
||||
char *value;
|
||||
char *equal_loc;
|
||||
bool result;
|
||||
|
||||
value = xstrdup(optarg);
|
||||
equal_loc = strchr(value, '=');
|
||||
if (!equal_loc)
|
||||
result = do_pset(value, NULL, &pset->popt, true);
|
||||
else {
|
||||
*equal_loc = '\0';
|
||||
result = do_pset(value, equal_loc+1, &pset->popt, true);
|
||||
}
|
||||
value = xstrdup(optarg);
|
||||
equal_loc = strchr(value, '=');
|
||||
if (!equal_loc)
|
||||
result = do_pset(value, NULL, &pset->popt, true);
|
||||
else
|
||||
{
|
||||
*equal_loc = '\0';
|
||||
result = do_pset(value, equal_loc + 1, &pset->popt, true);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
fprintf(stderr, "Couldn't set printing paramter %s.\n", value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!result)
|
||||
{
|
||||
fprintf(stderr, "Couldn't set printing paramter %s.\n", value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
free(value);
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
SetVariable(pset->vars, "quiet", "");
|
||||
break;
|
||||
case 's':
|
||||
SetVariable(pset->vars, "singlestep", "");
|
||||
break;
|
||||
case 'S':
|
||||
SetVariable(pset->vars, "singleline", "");
|
||||
break;
|
||||
case 't':
|
||||
pset->popt.topt.tuples_only = true;
|
||||
break;
|
||||
case 'T':
|
||||
pset->popt.topt.tableAttr = xstrdup(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
pset->getPassword = true;
|
||||
options->username = "?";
|
||||
break;
|
||||
case 'U':
|
||||
options->username = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
pset->popt.topt.expanded = true;
|
||||
break;
|
||||
case 'v':
|
||||
{
|
||||
char *value;
|
||||
char *equal_loc;
|
||||
free(value);
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
SetVariable(pset->vars, "quiet", "");
|
||||
break;
|
||||
case 's':
|
||||
SetVariable(pset->vars, "singlestep", "");
|
||||
break;
|
||||
case 'S':
|
||||
SetVariable(pset->vars, "singleline", "");
|
||||
break;
|
||||
case 't':
|
||||
pset->popt.topt.tuples_only = true;
|
||||
break;
|
||||
case 'T':
|
||||
pset->popt.topt.tableAttr = xstrdup(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
pset->getPassword = true;
|
||||
options->username = "?";
|
||||
break;
|
||||
case 'U':
|
||||
options->username = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
pset->popt.topt.expanded = true;
|
||||
break;
|
||||
case 'v':
|
||||
{
|
||||
char *value;
|
||||
char *equal_loc;
|
||||
|
||||
value = xstrdup(optarg);
|
||||
equal_loc = strchr(value, '=');
|
||||
if (!equal_loc) {
|
||||
if (!DeleteVariable(pset->vars, value)) {
|
||||
fprintf(stderr, "Couldn't delete variable %s.\n", value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
*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);
|
||||
}
|
||||
}
|
||||
value = xstrdup(optarg);
|
||||
equal_loc = strchr(value, '=');
|
||||
if (!equal_loc)
|
||||
{
|
||||
if (!DeleteVariable(pset->vars, value))
|
||||
{
|
||||
fprintf(stderr, "Couldn't delete variable %s.\n", value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*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);
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
options->action = ACT_SHOW_VER;
|
||||
break;
|
||||
case 'W':
|
||||
pset->getPassword = true;
|
||||
break;
|
||||
case '?':
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
free(value);
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
options->action = ACT_SHOW_VER;
|
||||
break;
|
||||
case 'W':
|
||||
pset->getPassword = true;
|
||||
break;
|
||||
case '?':
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
#ifndef HAVE_GETOPT_LONG
|
||||
case '-':
|
||||
fprintf(stderr, "This version of psql was compiled without support for long options.\n"
|
||||
"Use -? for help on invocation options.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
case '-':
|
||||
fprintf(stderr, "This version of psql was compiled without support for long options.\n"
|
||||
"Use -? for help on invocation options.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if we still have arguments, use it as the database name and username */
|
||||
while (argc - optind >= 1) {
|
||||
if (!options->dbname)
|
||||
options->dbname = argv[optind];
|
||||
else if (!options->username)
|
||||
options->username = argv[optind];
|
||||
else
|
||||
fprintf(stderr, "Warning: extra option %s ignored.\n", argv[optind]);
|
||||
/*
|
||||
* if we still have arguments, use it as the database name and
|
||||
* username
|
||||
*/
|
||||
while (argc - optind >= 1)
|
||||
{
|
||||
if (!options->dbname)
|
||||
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.
|
||||
*/
|
||||
static void
|
||||
process_psqlrc(PsqlSettings * pset)
|
||||
process_psqlrc(PsqlSettings *pset)
|
||||
{
|
||||
char *psqlrc;
|
||||
char * home;
|
||||
char *psqlrc;
|
||||
char *home;
|
||||
|
||||
#ifdef WIN32
|
||||
#define R_OK 0
|
||||
#endif
|
||||
|
||||
/* System-wide startup file */
|
||||
if (access("/etc/psqlrc-"PG_RELEASE"."PG_VERSION"."PG_SUBVERSION, R_OK) == 0)
|
||||
process_file("/etc/psqlrc-"PG_RELEASE"."PG_VERSION"."PG_SUBVERSION, pset);
|
||||
else if (access("/etc/psqlrc", R_OK) == 0)
|
||||
process_file("/etc/psqlrc", pset);
|
||||
/* System-wide startup file */
|
||||
if (access("/etc/psqlrc-" PG_RELEASE "." PG_VERSION "." PG_SUBVERSION, R_OK) == 0)
|
||||
process_file("/etc/psqlrc-" PG_RELEASE "." PG_VERSION "." PG_SUBVERSION, pset);
|
||||
else if (access("/etc/psqlrc", R_OK) == 0)
|
||||
process_file("/etc/psqlrc", pset);
|
||||
|
||||
/* Look for one in the home dir */
|
||||
home = getenv("HOME");
|
||||
/* Look for one in the home dir */
|
||||
home = getenv("HOME");
|
||||
|
||||
if (home) {
|
||||
psqlrc = (char *) malloc(strlen(home) + 20);
|
||||
if (!psqlrc) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
if (home)
|
||||
{
|
||||
psqlrc = (char *) malloc(strlen(home) + 20);
|
||||
if (!psqlrc)
|
||||
{
|
||||
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
|
||||
showVersion(PsqlSettings *pset, bool verbose)
|
||||
{
|
||||
PGresult *res;
|
||||
char *versionstr = NULL;
|
||||
long int release = 0, version = 0, subversion = 0;
|
||||
|
||||
/* get backend version */
|
||||
res = PSQLexec(pset, "SELECT version()");
|
||||
if (PQresultStatus(res) == PGRES_TUPLES_OK)
|
||||
versionstr = PQgetvalue(res, 0, 0);
|
||||
PGresult *res;
|
||||
char *versionstr = NULL;
|
||||
long int release = 0,
|
||||
version = 0,
|
||||
subversion = 0;
|
||||
|
||||
if (!verbose) {
|
||||
if (versionstr) puts(versionstr);
|
||||
PQclear(res);
|
||||
return;
|
||||
}
|
||||
/* get backend version */
|
||||
res = PSQLexec(pset, "SELECT version()");
|
||||
if (PQresultStatus(res) == PGRES_TUPLES_OK)
|
||||
versionstr = PQgetvalue(res, 0, 0);
|
||||
|
||||
if (strncmp(versionstr, "PostgreSQL ", 11) == 0) {
|
||||
char *tmp;
|
||||
release = strtol(&versionstr[11], &tmp, 10);
|
||||
version = strtol(tmp+1, &tmp, 10);
|
||||
subversion = strtol(tmp+1, &tmp, 10);
|
||||
}
|
||||
if (!verbose)
|
||||
{
|
||||
if (versionstr)
|
||||
puts(versionstr);
|
||||
PQclear(res);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Server: %s\npsql", versionstr ? versionstr : "(could not connected)");
|
||||
if (strncmp(versionstr, "PostgreSQL ", 11) == 0)
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
if (strcmp(versionstr, PG_VERSION_STR) != 0)
|
||||
printf(&PG_VERSION_STR[strcspn(PG_VERSION_STR, " ")]);
|
||||
printf(" ("__DATE__" "__TIME__")");
|
||||
release = strtol(&versionstr[11], &tmp, 10);
|
||||
version = strtol(tmp + 1, &tmp, 10);
|
||||
subversion = strtol(tmp + 1, &tmp, 10);
|
||||
}
|
||||
|
||||
printf("Server: %s\npsql", versionstr ? versionstr : "(could not connected)");
|
||||
|
||||
if (strcmp(versionstr, PG_VERSION_STR) != 0)
|
||||
printf(&PG_VERSION_STR[strcspn(PG_VERSION_STR, " ")]);
|
||||
printf(" (" __DATE__ " " __TIME__ ")");
|
||||
|
||||
#ifdef MULTIBYTE
|
||||
printf(", multibyte");
|
||||
printf(", multibyte");
|
||||
#endif
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
printf(", long options");
|
||||
printf(", long options");
|
||||
#endif
|
||||
#ifdef USE_READLINE
|
||||
printf(", readline");
|
||||
printf(", readline");
|
||||
#endif
|
||||
#ifdef USE_HISTORY
|
||||
printf(", history");
|
||||
printf(", history");
|
||||
#endif
|
||||
#ifdef USE_LOCALE
|
||||
printf(", locale");
|
||||
printf(", locale");
|
||||
#endif
|
||||
#ifdef PSQL_ALWAYS_GET_PASSWORDS
|
||||
printf(", always password");
|
||||
printf(", always password");
|
||||
#endif
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
printf(", assert checks");
|
||||
printf(", assert checks");
|
||||
#endif
|
||||
|
||||
puts("");
|
||||
puts("");
|
||||
|
||||
if (release < 6 || (release == 6 && version < 5))
|
||||
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"
|
||||
"distribution.");
|
||||
if (release < 6 || (release == 6 && version < 5))
|
||||
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"
|
||||
"distribution.");
|
||||
|
||||
PQclear(res);
|
||||
PQclear(res);
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
#include <c.h>
|
||||
#include "stringutils.h"
|
||||
|
||||
//#include <ctype.h>
|
||||
//
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
//#include <stdio.h>
|
||||
//
|
||||
#include <stdio.h>
|
||||
|
||||
#include <postgres.h>
|
||||
#ifndef HAVE_STRDUP
|
||||
|
@ -17,118 +19,130 @@
|
|||
|
||||
|
||||
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)
|
||||
*
|
||||
* The calling convention is similar to that of strtok.
|
||||
* s - string to parse, if NULL continue parsing the last string
|
||||
* delim - set of characters that delimit tokens (usually whitespace)
|
||||
* quote - set of characters that quote stuff, they're not part of the token
|
||||
* escape - character than can quote quotes
|
||||
* s - string to parse, if NULL continue parsing the last string
|
||||
* delim - set of characters that delimit tokens (usually whitespace)
|
||||
* quote - set of characters that quote stuff, they're not part of the token
|
||||
* escape - character than can quote quotes
|
||||
* 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
|
||||
* parsed string
|
||||
* token_pos - if not NULL, receives a count to the start of the token in the
|
||||
* parsed string
|
||||
*
|
||||
* Note that the string s is _not_ overwritten in this implementation.
|
||||
*/
|
||||
char * strtokx(const char *s,
|
||||
const char *delim,
|
||||
const char *quote,
|
||||
char escape,
|
||||
char * was_quoted,
|
||||
unsigned int * token_pos)
|
||||
char *
|
||||
strtokx(const char *s,
|
||||
const char *delim,
|
||||
const char *quote,
|
||||
char escape,
|
||||
char *was_quoted,
|
||||
unsigned int *token_pos)
|
||||
{
|
||||
static char * storage = NULL; /* store the local copy of the users string here */
|
||||
static char * string = NULL; /* pointer into storage where to continue on next call */
|
||||
/* variously abused variables: */
|
||||
unsigned int offset;
|
||||
char * start;
|
||||
char *cp = NULL;
|
||||
static char *storage = NULL;/* store the local copy of the users
|
||||
* string here */
|
||||
static char *string = NULL; /* pointer into storage where to continue
|
||||
* on next call */
|
||||
|
||||
if (s) {
|
||||
free(storage);
|
||||
storage = strdup(s);
|
||||
string = storage;
|
||||
}
|
||||
/* variously abused variables: */
|
||||
unsigned int offset;
|
||||
char *start;
|
||||
char *cp = NULL;
|
||||
|
||||
if (!storage)
|
||||
return NULL;
|
||||
if (s)
|
||||
{
|
||||
free(storage);
|
||||
storage = strdup(s);
|
||||
string = storage;
|
||||
}
|
||||
|
||||
/* skip leading "whitespace" */
|
||||
offset = strspn(string, delim);
|
||||
if (!storage)
|
||||
return NULL;
|
||||
|
||||
/* end of string reached */
|
||||
if (string[offset] == '\0') {
|
||||
/* technically we don't need to free here, but we're nice */
|
||||
free(storage);
|
||||
storage = NULL;
|
||||
string = NULL;
|
||||
return NULL;
|
||||
}
|
||||
/* skip leading "whitespace" */
|
||||
offset = strspn(string, delim);
|
||||
|
||||
/* test if quoting character */
|
||||
if (quote)
|
||||
cp = strchr(quote, string[offset]);
|
||||
/* end of string reached */
|
||||
if (string[offset] == '\0')
|
||||
{
|
||||
/* technically we don't need to free here, but we're nice */
|
||||
free(storage);
|
||||
storage = NULL;
|
||||
string = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cp) {
|
||||
/* okay, we have a quoting character, now scan for the closer */
|
||||
char *p;
|
||||
start = &string[offset+1];
|
||||
/* test if quoting character */
|
||||
if (quote)
|
||||
cp = strchr(quote, string[offset]);
|
||||
|
||||
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)
|
||||
*token_pos = start - storage;
|
||||
*token_pos = start - storage;
|
||||
|
||||
for(p = start;
|
||||
*p && (*p != *cp || *(p-1) == escape) ;
|
||||
#ifdef MULTIBYTE
|
||||
p += PQmblen(p)
|
||||
#else
|
||||
p++
|
||||
#endif
|
||||
);
|
||||
offset = strcspn(start, delim);
|
||||
if (was_quoted)
|
||||
*was_quoted = 0;
|
||||
|
||||
/* 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;
|
||||
if (start[offset] != '\0')
|
||||
{
|
||||
start[offset] = '\0';
|
||||
string = &start[offset] + 1;
|
||||
|
||||
return start;
|
||||
}
|
||||
else {
|
||||
if (was_quoted)
|
||||
*was_quoted = *cp;
|
||||
string = p;
|
||||
|
||||
unescape_quotes (start, *cp, escape);
|
||||
return start;
|
||||
else
|
||||
{
|
||||
string = &start[offset];
|
||||
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
|
||||
unescape_quotes(char *source, char quote, char escape)
|
||||
{
|
||||
char *p;
|
||||
char *destination, *tmp;
|
||||
char *p;
|
||||
char *destination,
|
||||
*tmp;
|
||||
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
assert(source);
|
||||
assert(source);
|
||||
#endif
|
||||
|
||||
destination = (char *) calloc(1, strlen(source)+1);
|
||||
if (!destination) {
|
||||
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++;
|
||||
destination = (char *) calloc(1, strlen(source) + 1);
|
||||
if (!destination)
|
||||
{
|
||||
perror("calloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
c = *p;
|
||||
|
||||
*tmp = c;
|
||||
tmp ++;
|
||||
}
|
||||
tmp = destination;
|
||||
|
||||
/* Terminating null character */
|
||||
*tmp = '\0';
|
||||
for (p = source; *p; p++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
|
||||
/* The cooler version of strtok() which knows about quotes and doesn't
|
||||
* overwrite your input */
|
||||
extern char *
|
||||
strtokx(const char *s,
|
||||
const char *delim,
|
||||
const char *quote,
|
||||
char escape,
|
||||
char * was_quoted,
|
||||
unsigned int * token_pos);
|
||||
extern char *strtokx(const char *s,
|
||||
const char *delim,
|
||||
const char *quote,
|
||||
char escape,
|
||||
char *was_quoted,
|
||||
unsigned int *token_pos);
|
||||
|
||||
#endif /* STRINGUTILS_H */
|
||||
|
|
|
@ -6,127 +6,145 @@
|
|||
#include <assert.h>
|
||||
|
||||
|
||||
VariableSpace CreateVariableSpace(void)
|
||||
VariableSpace
|
||||
CreateVariableSpace(void)
|
||||
{
|
||||
struct _variable *ptr;
|
||||
struct _variable *ptr;
|
||||
|
||||
ptr = calloc(1, sizeof *ptr);
|
||||
if (!ptr) return NULL;
|
||||
ptr = calloc(1, sizeof *ptr);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
ptr->name = strdup("@");
|
||||
ptr->value = strdup("");
|
||||
if (!ptr->name || !ptr->value) {
|
||||
free(ptr->name);
|
||||
free(ptr->value);
|
||||
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;
|
||||
ptr->name = strdup("@");
|
||||
ptr->value = strdup("");
|
||||
if (!ptr->name || !ptr->value)
|
||||
{
|
||||
free(ptr->name);
|
||||
free(ptr->value);
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool DeleteVariable(VariableSpace space, const char * name)
|
||||
const char *
|
||||
GetVariable(VariableSpace space, const char *name)
|
||||
{
|
||||
struct _variable *current, *previous;
|
||||
struct _variable *current;
|
||||
|
||||
if (!space)
|
||||
return false;
|
||||
if (!space)
|
||||
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
|
||||
assert(current->name);
|
||||
assert(current->value);
|
||||
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;
|
||||
if (strcmp(current->name, name) == 0)
|
||||
return current->value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DestroyVariableSpace(VariableSpace space)
|
||||
bool
|
||||
GetVariableBool(VariableSpace space, const char *name)
|
||||
{
|
||||
if (!space)
|
||||
return;
|
||||
|
||||
DestroyVariableSpace(space->next);
|
||||
free(space);
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -13,21 +13,22 @@
|
|||
|
||||
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
|
||||
|
||||
struct _variable {
|
||||
char * name;
|
||||
char * value;
|
||||
struct _variable * next;
|
||||
struct _variable
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
struct _variable *next;
|
||||
};
|
||||
|
||||
typedef struct _variable * VariableSpace;
|
||||
typedef struct _variable *VariableSpace;
|
||||
|
||||
|
||||
VariableSpace CreateVariableSpace(void);
|
||||
const char * GetVariable(VariableSpace space, const char * name);
|
||||
bool GetVariableBool(VariableSpace space, const char * name);
|
||||
bool SetVariable(VariableSpace space, const char * name, const char * value);
|
||||
bool DeleteVariable(VariableSpace space, const char * name);
|
||||
void DestroyVariableSpace(VariableSpace space);
|
||||
const char *GetVariable(VariableSpace space, const char *name);
|
||||
bool GetVariableBool(VariableSpace space, const char *name);
|
||||
bool SetVariable(VariableSpace space, const char *name, const char *value);
|
||||
bool DeleteVariable(VariableSpace space, const char *name);
|
||||
void DestroyVariableSpace(VariableSpace space);
|
||||
|
||||
|
||||
#endif /* VARIABLES_H */
|
||||
#endif /* VARIABLES_H */
|
||||
|
|
Loading…
Reference in New Issue