diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/psql/command.c | 118 | ||||
-rw-r--r-- | src/bin/psql/command.h | 18 | ||||
-rw-r--r-- | src/bin/psql/common.c | 180 | ||||
-rw-r--r-- | src/bin/psql/common.h | 6 | ||||
-rw-r--r-- | src/bin/psql/copy.c | 11 | ||||
-rw-r--r-- | src/bin/psql/copy.h | 3 | ||||
-rw-r--r-- | src/bin/psql/large_obj.c | 262 | ||||
-rw-r--r-- | src/bin/psql/prompt.c | 40 | ||||
-rw-r--r-- | src/bin/psql/settings.h | 6 | ||||
-rw-r--r-- | src/bin/psql/startup.c | 39 | ||||
-rw-r--r-- | src/bin/psql/variables.c | 45 | ||||
-rw-r--r-- | src/bin/psql/variables.h | 14 |
12 files changed, 380 insertions, 362 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index fd6193dd243..c105f0bd9d8 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3,7 +3,7 @@ * * Copyright 2000-2002 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.96 2003/05/14 03:26:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.97 2003/06/28 00:12:40 tgl Exp $ */ #include "postgres_fe.h" #include "command.h" @@ -457,20 +457,30 @@ exec_command(const char *cmd, char *encoding = scan_option(&string, OT_NORMAL, NULL, false); if (!encoding) - /* show encoding */ + { + /* show encoding --- first check for change sent from server */ + if (pset.encoding != PQclientEncoding(pset.db) && + PQclientEncoding(pset.db) >= 0) + { + pset.encoding = PQclientEncoding(pset.db); + pset.popt.topt.encoding = pset.encoding; + SetVariable(pset.vars, "ENCODING", + pg_encoding_to_char(pset.encoding)); + } puts(pg_encoding_to_char(pset.encoding)); + } else { /* set encoding */ if (PQsetClientEncoding(pset.db, encoding) == -1) psql_error("%s: invalid encoding name or conversion procedure not found\n", encoding); - else { /* save encoding info into psql internal data */ pset.encoding = PQclientEncoding(pset.db); - pset.popt.topt.encoding = PQclientEncoding(pset.db); - SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); + pset.popt.topt.encoding = pset.encoding; + SetVariable(pset.vars, "ENCODING", + pg_encoding_to_char(pset.encoding)); } free(encoding); } @@ -694,7 +704,13 @@ exec_command(const char *cmd, free(opt); } - if (!SetVariable(pset.vars, opt0, newval)) + if (SetVariable(pset.vars, opt0, newval)) + { + /* Check for special variables */ + if (strcmp(opt0, "VERBOSE") == 0) + SyncVerboseVariable(); + } + else { psql_error("\\%s: error\n", cmd); success = false; @@ -1327,11 +1343,7 @@ do_connect(const char *new_dbname, const char *new_user) bool success = false; /* Delete variables (in case we fail before setting them anew) */ - SetVariable(pset.vars, "DBNAME", NULL); - SetVariable(pset.vars, "USER", NULL); - SetVariable(pset.vars, "HOST", NULL); - SetVariable(pset.vars, "PORT", NULL); - SetVariable(pset.vars, "ENCODING", NULL); + UnsyncVariables(); /* If dbname is "" then use old name, else new one (even if NULL) */ if (oldconn && new_dbname && PQdb(oldconn) && strcmp(new_dbname, "") == 0) @@ -1429,51 +1441,75 @@ do_connect(const char *new_dbname, const char *new_user) } PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); - pset.encoding = PQclientEncoding(pset.db); - pset.popt.topt.encoding = PQclientEncoding(pset.db); /* Update variables */ + SyncVariables(); + + return success; +} + + +/* + * SyncVariables + * + * Make psql's internal variables agree with connection state upon + * establishing a new connection. + */ +void +SyncVariables(void) +{ + /* get stuff from connection */ + pset.encoding = PQclientEncoding(pset.db); + pset.popt.topt.encoding = pset.encoding; + SetVariable(pset.vars, "DBNAME", PQdb(pset.db)); SetVariable(pset.vars, "USER", PQuser(pset.db)); SetVariable(pset.vars, "HOST", PQhost(pset.db)); SetVariable(pset.vars, "PORT", PQport(pset.db)); SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); - pset.issuper = test_superuser(PQuser(pset.db)); - - return success; + /* send stuff to it, too */ + SyncVerboseVariable(); } - - /* - * Test if the given user is a database superuser. - * (Is used to set up the prompt right.) + * UnsyncVariables + * + * Clear variables that should be not be set when there is no connection. */ -bool -test_superuser(const char *username) +void +UnsyncVariables(void) { - PGresult *res; - PQExpBufferData buf; - bool answer; - - if (!username) - return false; - - initPQExpBuffer(&buf); - printfPQExpBuffer(&buf, "SELECT usesuper FROM pg_catalog.pg_user WHERE usename = '%s'", username); - res = PSQLexec(buf.data, true); - termPQExpBuffer(&buf); - - answer = - (res && PQntuples(res) > 0 && PQnfields(res) > 0 - && !PQgetisnull(res, 0, 0) - && PQgetvalue(res, 0, 0) - && strcmp(PQgetvalue(res, 0, 0), "t") == 0); - PQclear(res); - return answer; + SetVariable(pset.vars, "DBNAME", NULL); + SetVariable(pset.vars, "USER", NULL); + SetVariable(pset.vars, "HOST", NULL); + SetVariable(pset.vars, "PORT", NULL); + SetVariable(pset.vars, "ENCODING", NULL); } +/* + * Update connection state from VERBOSE variable + */ +void +SyncVerboseVariable(void) +{ + switch (SwitchVariable(pset.vars, "VERBOSE", + "default", "terse", "verbose", NULL)) + { + case 1: /* default */ + PQsetErrorVerbosity(pset.db, PQERRORS_DEFAULT); + break; + case 2: /* terse */ + PQsetErrorVerbosity(pset.db, PQERRORS_TERSE); + break; + case 3: /* verbose */ + PQsetErrorVerbosity(pset.db, PQERRORS_VERBOSE); + break; + default: /* not set or unrecognized value */ + PQsetErrorVerbosity(pset.db, PQERRORS_DEFAULT); + break; + } +} /* diff --git a/src/bin/psql/command.h b/src/bin/psql/command.h index f74be3a8510..ead859cd32c 100644 --- a/src/bin/psql/command.h +++ b/src/bin/psql/command.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/command.h,v 1.14 2002/03/27 19:16:13 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/command.h,v 1.15 2003/06/28 00:12:40 tgl Exp $ */ #ifndef COMMAND_H #define COMMAND_H @@ -26,20 +26,22 @@ typedef enum _backslashResult } backslashResult; -backslashResult HandleSlashCmds(const char *line, +extern backslashResult HandleSlashCmds(const char *line, PQExpBuffer query_buf, const char **end_of_cmd, volatile int *paren_level); -int - process_file(char *filename); +extern int process_file(char *filename); -bool - test_superuser(const char *username); - -bool do_pset(const char *param, +extern bool do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet); +extern void SyncVariables(void); + +extern void UnsyncVariables(void); + +extern void SyncVerboseVariable(void); + #endif /* COMMAND_H */ diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 84dc32bd5ca..96f3c281007 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -3,11 +3,12 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.64 2003/06/12 08:15:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.65 2003/06/28 00:12:40 tgl Exp $ */ #include "postgres_fe.h" #include "common.h" +#include <ctype.h> #include <errno.h> #include <stdarg.h> #ifndef HAVE_STRDUP @@ -29,6 +30,7 @@ #include "settings.h" #include "variables.h" +#include "command.h" #include "copy.h" #include "prompt.h" #include "print.h" @@ -55,6 +57,10 @@ typedef struct _timeb TimevalStruct; extern bool prompt_state; + +static bool is_transact_command(const char *query); + + /* * "Safe" wrapper around strdup() */ @@ -195,8 +201,8 @@ handle_sigint(SIGNAL_ARGS) { int save_errno = errno; - /* Don't muck around if copying in or prompting for a password. */ - if ((copy_in_state && pset.cur_cmd_interactive) || prompt_state) + /* Don't muck around if prompting for a password. */ + if (prompt_state) return; if (cancelConn == NULL) @@ -262,11 +268,7 @@ CheckConnection() PQfinish(pset.db); pset.db = NULL; ResetCancelConn(); - SetVariable(pset.vars, "DBNAME", NULL); - SetVariable(pset.vars, "HOST", NULL); - SetVariable(pset.vars, "PORT", NULL); - SetVariable(pset.vars, "USER", NULL); - SetVariable(pset.vars, "ENCODING", NULL); + UnsyncVariables(); } else fputs(gettext("Succeeded.\n"), stderr); @@ -304,8 +306,8 @@ void ResetCancelConn(void) * AcceptResult * * Checks whether a result is valid, giving an error message if necessary; - * (re)sets copy_in_state and cancelConn as needed, and ensures that the - * connection to the backend is still up. + * resets cancelConn as needed, and ensures that the connection to the backend + * is still up. * * Returns true for valid result, false for error state. */ @@ -322,12 +324,9 @@ AcceptResult(const PGresult *result) } else switch (PQresultStatus(result)) { - case PGRES_COPY_IN: - copy_in_state = true; - break; - case PGRES_COMMAND_OK: case PGRES_TUPLES_OK: + case PGRES_COPY_IN: /* Fine, do nothing */ break; @@ -358,18 +357,15 @@ AcceptResult(const PGresult *result) * This is the way to send "backdoor" queries (those not directly entered * by the user). It is subject to -E but not -e. * - * If the given querystring generates multiple PGresults, normally the last - * one is returned to the caller. However, if ignore_command_ok is TRUE, - * then PGresults with status PGRES_COMMAND_OK are ignored. This is intended - * mainly to allow locutions such as "begin; select ...; commit". + * In autocommit-off mode, a new transaction block is started if start_xact + * is true; nothing special is done when start_xact is false. Typically, + * start_xact = false is used for SELECTs and explicit BEGIN/COMMIT commands. */ PGresult * -PSQLexec(const char *query, bool ignore_command_ok) +PSQLexec(const char *query, bool start_xact) { - PGresult *res = NULL; - PGresult *newres; + PGresult *res; int echo_hidden; - ExecStatusType rstatus; if (!pset.db) { @@ -378,50 +374,40 @@ PSQLexec(const char *query, bool ignore_command_ok) } echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL); - if (echo_hidden != var_notset) + if (echo_hidden != VAR_NOTSET) { printf("********* QUERY **********\n" "%s\n" "**************************\n\n", query); fflush(stdout); - if (echo_hidden == 1) - return NULL; + if (echo_hidden == 1) /* noexec? */ + return NULL; } - /* discard any uneaten results of past queries */ - while ((newres = PQgetResult(pset.db)) != NULL) - PQclear(newres); - SetCancelConn(); - if (!PQsendQuery(pset.db, query)) - { - psql_error("%s", PQerrorMessage(pset.db)); - ResetCancelConn(); - return NULL; - } - - rstatus = PGRES_EMPTY_QUERY; - while (rstatus != PGRES_COPY_IN && - rstatus != PGRES_COPY_OUT && - (newres = PQgetResult(pset.db))) + if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE && + !GetVariableBool(pset.vars, "AUTOCOMMIT")) + { + res = PQexec(pset.db, "BEGIN"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { - rstatus = PQresultStatus(newres); - if (!ignore_command_ok || rstatus != PGRES_COMMAND_OK) - { - PGresult *tempRes = res; - res = newres; - newres = tempRes; + psql_error("%s", PQerrorMessage(pset.db)); + PQclear(res); + ResetCancelConn(); + return NULL; } - PQclear(newres); + PQclear(res); } + res = PQexec(pset.db, query); + if (!AcceptResult(res) && res) { PQclear(res); res = NULL; - } + } return res; } @@ -613,6 +599,21 @@ SendQuery(const char *query) SetCancelConn(); + if (PQtransactionStatus(pset.db) == PQTRANS_IDLE && + !GetVariableBool(pset.vars, "AUTOCOMMIT") && + !is_transact_command(query)) + { + results = PQexec(pset.db, "BEGIN"); + if (PQresultStatus(results) != PGRES_COMMAND_OK) + { + psql_error("%s", PQerrorMessage(pset.db)); + PQclear(results); + ResetCancelConn(); + return false; + } + PQclear(results); + } + if (pset.timing) GETTIMEOFDAY(&before); results = PQexec(pset.db, query); @@ -626,6 +627,68 @@ SendQuery(const char *query) return OK; } +/* + * check whether a query string begins with BEGIN/COMMIT/ROLLBACK/START XACT + */ +static bool +is_transact_command(const char *query) +{ + int wordlen; + + /* + * First we must advance over any whitespace and comments. + */ + while (*query) + { + if (isspace((unsigned char) *query)) + query++; + else if (query[0] == '-' && query[1] == '-') + { + query += 2; + while (*query && *query != '\n') + query++; + } + else if (query[0] == '/' && query[1] == '*') + { + query += 2; + while (*query) + { + if (query[0] == '*' && query[1] == '/') + { + query += 2; + break; + } + else + query++; + } + } + else + break; /* found first token */ + } + + /* + * Check word length ("beginx" is not "begin"). + */ + wordlen = 0; + while (isalpha((unsigned char) query[wordlen])) + wordlen++; + + if (wordlen == 5 && strncasecmp(query, "begin", 5) == 0) + return true; + if (wordlen == 6 && strncasecmp(query, "commit", 6) == 0) + return true; + if (wordlen == 8 && strncasecmp(query, "rollback", 8) == 0) + return true; + if (wordlen == 5 && strncasecmp(query, "abort", 5) == 0) + return true; + if (wordlen == 3 && strncasecmp(query, "end", 3) == 0) + return true; + if (wordlen == 5 && strncasecmp(query, "start", 5) == 0) + return true; + + return false; +} + char parse_char(char **buf) { @@ -637,3 +700,24 @@ char parse_char(char **buf) } +/* + * Test if the current user is a database superuser. + * + * Note: this will correctly detect superuserness only with a protocol-3.0 + * or newer backend; otherwise it will always say "false". + */ +bool +is_superuser(void) +{ + const char *val; + + if (!pset.db) + return false; + + val = PQparameterStatus(pset.db, "is_superuser"); + + if (val && strcmp(val, "on") == 0) + return true; + + return false; +} diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index d29b317d23a..2f38ea371b0 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/common.h,v 1.25 2003/03/20 15:44:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/common.h,v 1.26 2003/06/28 00:12:40 tgl Exp $ */ #ifndef COMMON_H #define COMMON_H @@ -34,10 +34,12 @@ extern void ResetCancelConn(void); extern void handle_sigint(SIGNAL_ARGS); #endif /* not WIN32 */ -extern PGresult *PSQLexec(const char *query, bool ignore_command_ok); +extern PGresult *PSQLexec(const char *query, bool start_xact); extern bool SendQuery(const char *query); +extern bool is_superuser(void); + /* sprompt.h */ extern char *simple_prompt(const char *prompt, int maxlen, bool echo); diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index 00664ea05d0..11f66c68978 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/copy.c,v 1.29 2003/03/20 06:00:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/copy.c,v 1.30 2003/06/28 00:12:40 tgl Exp $ */ #include "postgres_fe.h" #include "copy.h" @@ -32,8 +32,6 @@ #define S_ISDIR(mode) __S_ISTYPE((mode), S_IFDIR) #endif -bool copy_in_state; - /* * parse_slash_copy * -- parses \copy command line @@ -395,7 +393,7 @@ do_copy(const char *args) return false; } - result = PSQLexec(query.data, false); + result = PSQLexec(query.data, true); termPQExpBuffer(&query); switch (PQresultStatus(result)) @@ -506,10 +504,6 @@ handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt) int ret; unsigned int linecount = 0; -#ifdef USE_ASSERT_CHECKING - assert(copy_in_state); -#endif - if (prompt) /* disable prompt if not interactive */ { if (!isatty(fileno(copystream))) @@ -563,7 +557,6 @@ handleCopyIn(PGconn *conn, FILE *copystream, const char *prompt) linecount++; } ret = !PQendcopy(conn); - copy_in_state = false; pset.lineno += linecount; return ret; } diff --git a/src/bin/psql/copy.h b/src/bin/psql/copy.h index c22146707ae..8f5c70d43c0 100644 --- a/src/bin/psql/copy.h +++ b/src/bin/psql/copy.h @@ -3,14 +3,13 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/copy.h,v 1.11 2001/10/28 06:25:58 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/copy.h,v 1.12 2003/06/28 00:12:40 tgl Exp $ */ #ifndef COPY_H #define COPY_H #include "libpq-fe.h" -extern bool copy_in_state; /* handler for \copy */ bool do_copy(const char *args); diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index 3d0b3a6eb96..a565d204784 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -3,7 +3,7 @@ * * Copyright 2000-2002 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.26 2003/06/27 16:55:23 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.27 2003/06/28 00:12:40 tgl Exp $ */ #include "postgres_fe.h" #include "large_obj.h" @@ -20,70 +20,95 @@ /* - * 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 - * progress. Rollback is default. + * Prepare to do a large-object operation. We *must* be inside a transaction + * block for all these operations, so start one if needed. + * + * Returns TRUE if okay, FALSE if failed. *own_transaction is set to indicate + * if we started our own transaction or not. */ +static bool +start_lo_xact(const char *operation, bool *own_transaction) +{ + PGTransactionStatusType tstatus; + PGresult *res; -static char notice[80]; + *own_transaction = false; -static void -_my_notice_handler(void *arg, const char *message) -{ - (void) arg; - strncpy(notice, message, 79); - notice[79] = '\0'; -} + if (!pset.db) + { + psql_error("%s: not connected to a database\n", operation); + return false; + } + + tstatus = PQtransactionStatus(pset.db); + + switch (tstatus) + { + case PQTRANS_IDLE: + /* need to start our own xact */ + if (!(res = PSQLexec("BEGIN", false))) + return false; + PQclear(res); + *own_transaction = true; + break; + case PQTRANS_INTRANS: + /* use the existing xact */ + break; + case PQTRANS_INERROR: + psql_error("%s: current transaction is aborted\n", operation); + return false; + default: + psql_error("%s: unknown transaction status\n", operation); + return false; + } + return true; +} +/* + * Clean up after a successful LO operation + */ static bool -handle_transaction(void) +finish_lo_xact(const char *operation, bool own_transaction) { PGresult *res; - bool commit = false; - PQnoticeProcessor old_notice_hook; - switch (SwitchVariable(pset.vars, "LO_TRANSACTION", - "nothing", - "commit", - NULL)) + if (own_transaction && + GetVariableBool(pset.vars, "AUTOCOMMIT")) { - case 1: /* nothing */ - return true; - case 2: /* commit */ - commit = true; - break; + /* close out our own xact */ + if (!(res = PSQLexec("COMMIT", false))) + { + res = PSQLexec("ROLLBACK", false); + PQclear(res); + return false; + } + PQclear(res); } - notice[0] = '\0'; - old_notice_hook = PQsetNoticeProcessor(pset.db, _my_notice_handler, NULL); + return true; +} - res = PSQLexec(commit ? "COMMIT" : "ROLLBACK", false); - if (!res) - return false; +/* + * Clean up after a failed LO operation + */ +static bool +fail_lo_xact(const char *operation, bool own_transaction) +{ + PGresult *res; - if (notice[0]) + if (own_transaction && + GetVariableBool(pset.vars, "AUTOCOMMIT")) { - if ((!commit && strcmp(notice, "WARNING: ROLLBACK: no transaction in progress\n") != 0) || - (commit && strcmp(notice, "WARNING: COMMIT: no transaction in progress\n") != 0)) - fputs(notice, stderr); - } - else if (!QUIET()) - { - if (commit) - puts(gettext("Warning: Your transaction in progress has been committed.")); - else - puts(gettext("Warning: Your transaction in progress has been rolled back.")); + /* close out our own xact */ + res = PSQLexec("ROLLBACK", false); + PQclear(res); } - PQsetNoticeProcessor(pset.db, old_notice_hook, NULL); - PQclear(res); - return true; + return false; /* always */ } - /* * do_lo_export() * @@ -92,53 +117,22 @@ handle_transaction(void) bool do_lo_export(const char *loid_arg, const char *filename_arg) { - PGresult *res; int status; bool own_transaction; - own_transaction = !VariableEquals(pset.vars, "LO_TRANSACTION", "nothing"); - - if (!pset.db) - { - psql_error("\\lo_export: not connected to a database\n"); + if (!start_lo_xact("\\lo_export", &own_transaction)) return false; - } - - if (own_transaction) - { - if (!handle_transaction()) - return false; - - if (!(res = PSQLexec("BEGIN", false))) - return false; - - PQclear(res); - } status = lo_export(pset.db, atooid(loid_arg), 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; + return fail_lo_xact("\\lo_export", own_transaction); } - if (own_transaction) - { - if (!(res = PSQLexec("COMMIT", false))) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - return false; - } - - PQclear(res); - } + if (!finish_lo_xact("\\lo_export", own_transaction)) + return false; fprintf(pset.queryFout, "lo_export\n"); @@ -146,7 +140,6 @@ do_lo_export(const char *loid_arg, const char *filename_arg) } - /* * do_lo_import() * @@ -161,41 +154,20 @@ do_lo_import(const char *filename_arg, const char *comment_arg) unsigned int i; bool own_transaction; - own_transaction = !VariableEquals(pset.vars, "LO_TRANSACTION", "nothing"); - - if (!pset.db) - { - psql_error("\\lo_import: not connected to a database\n"); + if (!start_lo_xact("\\lo_import", &own_transaction)) return false; - } - - if (own_transaction) - { - if (!handle_transaction()) - return false; - - if (!(res = PSQLexec("BEGIN", false))) - return false; - - PQclear(res); - } loid = lo_import(pset.db, filename_arg); if (loid == InvalidOid) { fputs(PQerrorMessage(pset.db), stderr); - if (own_transaction) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - } - return false; + return fail_lo_xact("\\lo_import", own_transaction); } /* insert description if given */ /* XXX don't try to hack pg_description if not superuser */ /* XXX ought to replace this with some kind of COMMENT command */ - if (comment_arg && pset.issuper) + if (comment_arg && is_superuser()) { char *cmdbuf; char *bufptr; @@ -203,14 +175,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg) cmdbuf = malloc(slen * 2 + 256); if (!cmdbuf) - { - if (own_transaction) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - } - return false; - } + return fail_lo_xact("\\lo_import", own_transaction); sprintf(cmdbuf, "INSERT INTO pg_catalog.pg_description VALUES ('%u', " "'pg_catalog.pg_largeobject'::regclass, " @@ -227,31 +192,16 @@ do_lo_import(const char *filename_arg, const char *comment_arg) if (!(res = PSQLexec(cmdbuf, false))) { - if (own_transaction) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - } free(cmdbuf); - return false; + return fail_lo_xact("\\lo_import", own_transaction); } PQclear(res); free(cmdbuf); } - if (own_transaction) - { - if (!(res = PSQLexec("COMMIT", false))) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - return false; - } - - PQclear(res); - } - + if (!finish_lo_xact("\\lo_import", own_transaction)) + return false; fprintf(pset.queryFout, "lo_import %u\n", loid); sprintf(oidbuf, "%u", loid); @@ -261,7 +211,6 @@ do_lo_import(const char *filename_arg, const char *comment_arg) } - /* * do_lo_unlink() * @@ -276,69 +225,32 @@ do_lo_unlink(const char *loid_arg) char buf[256]; bool own_transaction; - own_transaction = !VariableEquals(pset.vars, "LO_TRANSACTION", "nothing"); - - if (!pset.db) - { - psql_error("\\lo_unlink: not connected to a database\n"); + if (!start_lo_xact("\\lo_unlink", &own_transaction)) return false; - } - - if (own_transaction) - { - if (!handle_transaction()) - return false; - - if (!(res = PSQLexec("BEGIN", false))) - return false; - - PQclear(res); - } status = lo_unlink(pset.db, loid); if (status == -1) { fputs(PQerrorMessage(pset.db), stderr); - if (own_transaction) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - } - return false; + return fail_lo_xact("\\lo_unlink", own_transaction); } /* remove the comment as well */ /* XXX don't try to hack pg_description if not superuser */ /* XXX ought to replace this with some kind of COMMENT command */ - if (pset.issuper) + if (is_superuser()) { snprintf(buf, sizeof(buf), "DELETE FROM pg_catalog.pg_description WHERE objoid = '%u' " "AND classoid = 'pg_catalog.pg_largeobject'::regclass", loid); if (!(res = PSQLexec(buf, false))) - { - if (own_transaction) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - } - return false; - } - PQclear(res); - } - - if (own_transaction) - { - if (!(res = PSQLexec("COMMIT", false))) - { - res = PQexec(pset.db, "ROLLBACK"); - PQclear(res); - return false; - } + return fail_lo_xact("\\lo_unlink", own_transaction); PQclear(res); } + if (!finish_lo_xact("\\lo_unlink", own_transaction)) + return false; fprintf(pset.queryFout, "lo_unlink %u\n", loid); diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index f9d66b752ec..0df16d4b0c6 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/prompt.c,v 1.25 2003/04/04 20:42:13 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/prompt.c,v 1.26 2003/06/28 00:12:40 tgl Exp $ */ #include "postgres_fe.h" #include "prompt.h" @@ -44,6 +44,7 @@ * or a ! if session is not connected to a database; * in prompt2 -, *, ', or "; * in prompt3 nothing + * %T - transaction status: empty, *, !, ? (unknown or no connection) * %? - the error code of the last query (not yet implemented) * %% - a percent sign * @@ -172,7 +173,7 @@ get_prompt(promptStatus_t status) case '8': case '9': *buf = parse_char((char **)&p); - break; + break; case 'R': switch (status) @@ -204,20 +205,39 @@ get_prompt(promptStatus_t status) buf[0] = '\0'; break; } + break; + + case 'T': + if (!pset.db) + buf[0] = '?'; + else switch (PQtransactionStatus(pset.db)) + { + case PQTRANS_IDLE: + buf[0] = '\0'; + break; + case PQTRANS_ACTIVE: + case PQTRANS_INTRANS: + buf[0] = '*'; + break; + case PQTRANS_INERROR: + buf[0] = '!'; + break; + default: + buf[0] = '?'; + break; + } + break; case '?': /* not here yet */ break; case '#': - { - if (pset.issuper) - buf[0] = '#'; - else - buf[0] = '>'; - - break; - } + if (is_superuser()) + buf[0] = '#'; + else + buf[0] = '>'; + break; /* execute command */ case '`': diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index cb41920eb9d..a471184fb21 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/settings.h,v 1.13 2002/03/05 00:01:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/settings.h,v 1.14 2003/06/28 00:12:40 tgl Exp $ */ #ifndef SETTINGS_H #define SETTINGS_H @@ -48,9 +48,7 @@ typedef struct _psqlSettings char *inputfile; /* for error reporting */ unsigned lineno; /* also for error reporting */ - bool issuper; /* is the current user a superuser? (used - * to form the prompt) */ - bool timing; /* timing of all queries */ + bool timing; /* enable timing of all queries */ } PsqlSettings; extern PsqlSettings pset; diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 74d79fc5078..b36aaa3476d 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.73 2003/04/04 20:42:13 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.74 2003/06/28 00:12:40 tgl Exp $ */ #include "postgres_fe.h" @@ -74,18 +74,13 @@ struct adhoc_opts bool no_psqlrc; }; -static void - parse_psql_options(int argc, char *argv[], struct adhoc_opts * options); - -static void - process_psqlrc(void); - -static void - showVersion(void); +static void parse_psql_options(int argc, char *argv[], + struct adhoc_opts * options); +static void process_psqlrc(void); +static void showVersion(void); #ifdef USE_SSL -static void - printSSLInfo(void); +static void printSSLInfo(void); #endif @@ -144,6 +139,10 @@ main(int argc, char *argv[]) SetVariable(pset.vars, "VERSION", PG_VERSION_STR); + /* Default values for variables that are used in noninteractive cases */ + SetVariableBool(pset.vars, "AUTOCOMMIT"); + SetVariable(pset.vars, "VERBOSE", "default"); + pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); /* This is obsolete and should be removed sometime. */ @@ -208,11 +207,7 @@ main(int argc, char *argv[]) PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); - /* - * We need to save the encoding because we want to have it available - * even if the database connection goes bad. - */ - pset.encoding = PQclientEncoding(pset.db); + SyncVariables(); if (options.action == ACT_LIST_DB) { @@ -222,14 +217,6 @@ main(int argc, char *argv[]) exit(success ? EXIT_SUCCESS : EXIT_FAILURE); } - SetVariable(pset.vars, "DBNAME", PQdb(pset.db)); - SetVariable(pset.vars, "USER", PQuser(pset.db)); - SetVariable(pset.vars, "HOST", PQhost(pset.db)); - SetVariable(pset.vars, "PORT", PQport(pset.db)); - SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); - - pset.popt.topt.encoding = pset.encoding; - /* * Now find something to do */ @@ -274,7 +261,6 @@ main(int argc, char *argv[]) */ else { - pset.issuper = test_superuser(PQuser(pset.db)); if (!QUIET() && !pset.notty) { printf(gettext("Welcome to %s %s, the PostgreSQL interactive terminal.\n\n" @@ -289,15 +275,18 @@ main(int argc, char *argv[]) #endif } + /* Default values for variables that are used in interactive case */ SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1); SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2); SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3); + if (!options.no_psqlrc) process_psqlrc(); if (!pset.notty) initializeInput(options.no_readline ? 0 : 1); if (options.action_string) /* -f - was used */ pset.inputfile = "<stdin>"; + successResult = MainLoop(stdin); } diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c index 35f84aefdcc..6b4c42786e7 100644 --- a/src/bin/psql/variables.c +++ b/src/bin/psql/variables.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/variables.c,v 1.10 2003/03/20 06:43:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/variables.c,v 1.11 2003/06/28 00:12:40 tgl Exp $ */ #include "postgres_fe.h" #include "variables.h" @@ -33,8 +33,6 @@ CreateVariableSpace(void) return ptr; } - - const char * GetVariable(VariableSpace space, const char *name) { @@ -59,14 +57,19 @@ GetVariable(VariableSpace space, const char *name) return NULL; } - - bool GetVariableBool(VariableSpace space, const char *name) { - return GetVariable(space, name) != NULL ? true : false; -} + const char *val; + val = GetVariable(space, name); + if (val == NULL) + return false; /* not set -> assume "off" */ + if (strcmp(val, "off") == 0) + return false; + /* for backwards compatibility, anything except "off" is taken as "true" */ + return true; +} bool VariableEquals(VariableSpace space, const char name[], const char value[]) @@ -76,7 +79,6 @@ VariableEquals(VariableSpace space, const char name[], const char value[]) return var && (strcmp(var, value) == 0); } - int GetVariableNum(VariableSpace space, const char name[], @@ -103,7 +105,6 @@ GetVariableNum(VariableSpace space, return result; } - int SwitchVariable(VariableSpace space, const char name[], const char *opt, ...) { @@ -117,17 +118,16 @@ SwitchVariable(VariableSpace space, const char name[], const char *opt, ...) va_start(args, opt); for (result=1; opt && (strcmp(var, opt) != 0); result++) opt = va_arg(args,const char *); - - if (!opt) result = var_notfound; + if (!opt) + result = VAR_NOTFOUND; va_end(args); } else - result = var_notset; + result = VAR_NOTSET; return result; } - void PrintVariables(VariableSpace space) { @@ -136,7 +136,6 @@ PrintVariables(VariableSpace space) printf("%s = '%s'\n", ptr->name, ptr->value); } - bool SetVariable(VariableSpace space, const char *name, const char *value) { @@ -176,16 +175,12 @@ SetVariable(VariableSpace space, const char *name, const char *value) return previous->next->value ? true : false; } - - bool SetVariableBool(VariableSpace space, const char *name) { - return SetVariable(space, name, ""); + return SetVariable(space, name, "on"); } - - bool DeleteVariable(VariableSpace space, const char *name) { @@ -217,15 +212,3 @@ DeleteVariable(VariableSpace space, const char *name) return true; } - - - -void -DestroyVariableSpace(VariableSpace space) -{ - if (!space) - return; - - DestroyVariableSpace(space->next); - free(space); -} diff --git a/src/bin/psql/variables.h b/src/bin/psql/variables.h index d297b5a4efa..ce239975ef3 100644 --- a/src/bin/psql/variables.h +++ b/src/bin/psql/variables.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/variables.h,v 1.11 2003/03/20 06:43:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/variables.h,v 1.12 2003/06/28 00:12:40 tgl Exp $ */ /* @@ -45,18 +45,18 @@ int GetVariableNum(VariableSpace space, /* Find value of variable <name> among NULL-terminated list of alternative - * options. Returns var_notset if the variable was not set, var_notfound if its - * value did not occur in the list of options, or the number of the matching - * option. The first option is 1, the second is 2 and so on. + * options. Returns VAR_NOTSET if the variable was not set, VAR_NOTFOUND + * if its value did not occur in the list of options, or the number of the + * matching option. The first option is 1, the second is 2 and so on. */ -enum { var_notset = 0, var_notfound = -1 }; -int SwitchVariable(VariableSpace space, const char name[], const char *opt,...); +enum { VAR_NOTSET = 0, VAR_NOTFOUND = -1 }; +int SwitchVariable(VariableSpace space, const char name[], + const char *opt, ...); void PrintVariables(VariableSpace space); bool SetVariable(VariableSpace space, const char *name, const char *value); bool SetVariableBool(VariableSpace space, const char *name); bool DeleteVariable(VariableSpace space, const char *name); -void DestroyVariableSpace(VariableSpace space); #endif /* VARIABLES_H */ |