diff options
Diffstat (limited to 'src/bin/psql/common.c')
-rw-r--r-- | src/bin/psql/common.c | 180 |
1 files changed, 132 insertions, 48 deletions
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; +} |