From be1c6e75293fac69453d2ca6fc99d642f7806dfa Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Thu, 20 Mar 2003 06:00:12 +0000 Subject: [PATCH] Here's some changes I made last night to psql's common.c (as found in 7.3.2). It removes some code duplication and #ifdeffing, and some unstructured ugliness such as tacky breaks and an unneeded continue. Breaks up a large function into smaller functions and reduces required nesting levels, and kills a variable or two. Jeroen T. Vermeulen --- src/bin/psql/common.c | 467 ++++++++++++++++++++++++---------------- src/bin/psql/common.h | 4 +- src/bin/psql/copy.c | 6 +- src/bin/psql/input.c | 101 +++++---- src/bin/psql/input.h | 5 +- src/bin/psql/mainloop.c | 5 +- 6 files changed, 359 insertions(+), 229 deletions(-) diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index e5a2c0bc8a..f3f7e80997 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.58 2003/03/20 04:49:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.59 2003/03/20 06:00:12 momjian Exp $ */ #include "postgres_fe.h" #include "common.h" @@ -34,6 +34,25 @@ #include "print.h" #include "mainloop.h" + +/* Workarounds for Windows */ +/* Probably to be moved up the source tree in the future, perhaps to be replaced by + * more specific checks like configure-style HAVE_GETTIMEOFDAY macros. + */ +#ifndef WIN32 + +typedef struct timeval TimevalStruct; +#define GETTIMEOFDAY(T) gettimeofday(T, NULL) +#define DIFF_MSEC(T, U) ((((T)->tv_sec - (U)->tv_sec) * 1000000.0 + (T)->tv_usec - (U)->tv_usec) / 1000.0) + +#else + +typedef struct _timeb TimevalStruct; +#define GETTIMEOFDAY(T) _ftime(&T) +#define DIFF_MSEC(T, U) ((((T)->time - (U)->time) * 1000.0 + (T)->millitm - (U)->millitm)) + +#endif + extern bool prompt_state; /* @@ -110,10 +129,7 @@ setQFout(const char *fname) /* Direct signals */ #ifndef WIN32 - if (pset.queryFoutPipe) - pqsignal(SIGPIPE, SIG_IGN); - else - pqsignal(SIGPIPE, SIG_DFL); + pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL); #endif return status; @@ -165,8 +181,10 @@ NoticeProcessor(void *arg, const char *message) * so. We use write() to print to stdout because it's better to use simple * facilities in a signal handler. */ -PGconn *cancelConn; -volatile bool cancel_pressed; +static PGconn *volatile cancelConn = NULL; + +volatile bool cancel_pressed = false; + #ifndef WIN32 @@ -198,6 +216,142 @@ handle_sigint(SIGNAL_ARGS) #endif /* not WIN32 */ + +/* ConnectionUp + * + * Returns whether our backend connection is still there. + */ +static bool +ConnectionUp() +{ + return PQstatus(pset.db) != CONNECTION_BAD; +} + + + +/* CheckConnection + * + * Verify that we still have a good connection to the backend, and if not, + * see if it can be restored. + * + * Returns true if either the connection was still there, or it could be + * restored successfully; false otherwise. If, however, there was no + * connection and the session is non-interactive, this will exit the program + * with a code of EXIT_BADCONN. + */ +static bool +CheckConnection() +{ + bool OK; + + OK = ConnectionUp(); + if (!OK) + { + if (!pset.cur_cmd_interactive) + { + psql_error("connection to server was lost\n"); + exit(EXIT_BADCONN); + } + + fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr); + PQreset(pset.db); + OK = ConnectionUp(); + if (!OK) + { + fputs(gettext("Failed.\n"), stderr); + 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); + } + else + fputs(gettext("Succeeded.\n"), stderr); + } + + return OK; +} + + + +/* + * SetCancelConn + * + * Set cancelConn to point to the current database connection. + */ +static void SetCancelConn(void) +{ + cancelConn = pset.db; +} + + +/* + * ResetCancelConn + * + * Set cancelConn to NULL. I don't know what this means exactly, but it saves + * having to export the variable. + */ +void ResetCancelConn(void) +{ + cancelConn = NULL; +} + + +/* + * 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. + * + * Returns true for valid result, false for error state. + */ +static bool +AcceptResult(const PGresult *result) +{ + bool OK = true; + + ResetCancelConn(); + + if (!result) + { + OK = false; + } + else switch (PQresultStatus(result)) + { + case PGRES_COPY_IN: + copy_in_state = true; + break; + + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + /* Fine, do nothing */ + break; + + case PGRES_COPY_OUT: + /* keep cancel connection for copy out state */ + SetCancelConn(); + break; + + default: + OK = false; + break; + } + + if (!OK) + { + CheckConnection(); + psql_error("%s", PQerrorMessage(pset.db)); + } + + return OK; +} + + + /* * PSQLexec * @@ -239,156 +393,68 @@ PSQLexec(const char *query, bool ignore_command_ok) while ((newres = PQgetResult(pset.db)) != NULL) PQclear(newres); - cancelConn = pset.db; - if (PQsendQuery(pset.db, query)) + SetCancelConn(); + if (!PQsendQuery(pset.db, query)) { - while ((newres = PQgetResult(pset.db)) != NULL) + 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))) { rstatus = PQresultStatus(newres); - if (ignore_command_ok && rstatus == PGRES_COMMAND_OK) + if (!ignore_command_ok || rstatus != PGRES_COMMAND_OK) { - PQclear(newres); - continue; - } - PQclear(res); + PGresult *tempRes = res; res = newres; - if (rstatus != PGRES_COPY_IN && - rstatus != PGRES_COPY_OUT) - break; + newres = tempRes; } + PQclear(newres); } - rstatus = PQresultStatus(res); - /* keep cancel connection for copy out state */ - if (rstatus != PGRES_COPY_OUT) - cancelConn = NULL; - if (rstatus == PGRES_COPY_IN) - copy_in_state = true; - - if (res && (rstatus == PGRES_COMMAND_OK || - rstatus == PGRES_TUPLES_OK || - rstatus == PGRES_COPY_IN || - rstatus == PGRES_COPY_OUT)) - return res; - else + + if (!AcceptResult(res) && res) { - psql_error("%s", PQerrorMessage(pset.db)); PQclear(res); - - if (PQstatus(pset.db) == CONNECTION_BAD) - { - if (!pset.cur_cmd_interactive) - { - psql_error("connection to server was lost\n"); - exit(EXIT_BADCONN); - } - fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr); - PQreset(pset.db); - if (PQstatus(pset.db) == CONNECTION_BAD) - { - fputs(gettext("Failed.\n"), stderr); - PQfinish(pset.db); - pset.db = NULL; - 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); - } - else - fputs(gettext("Succeeded.\n"), stderr); + res = NULL; } - return NULL; - } + return res; } /* - * SendQuery: send the query string to the backend - * (and print out results) + * PrintNotifications: check for asynchronous notifications, and print them out * - * Note: This is the "front door" way to send a query. That is, use it to - * send queries actually entered by the user. These queries will be subject to - * single step mode. - * To send "back door" queries (generated by slash commands, etc.) in a - * controlled way, use PSQLexec(). - * - * Returns true if the query executed successfully, false otherwise. */ -bool -SendQuery(const char *query) +static void +PrintNotifications(void) { - bool success = false; - PGresult *results; PGnotify *notify; -#ifndef WIN32 - struct timeval before, - after; -#else - struct _timeb before, - after; -#endif - - if (!pset.db) - { - psql_error("You are currently not connected to a database.\n"); - return false; - } - - if (GetVariableBool(pset.vars, "SINGLESTEP")) - { - char buf[3]; - printf(gettext("***(Single step mode: Verify query)*********************************************\n" - "%s\n" - "***(press return to proceed or enter x and return to cancel)********************\n"), - query); - fflush(stdout); - if (fgets(buf, sizeof(buf), stdin) != NULL) - if (buf[0] == 'x') - return false; - } - else + while ((notify = PQnotifies(pset.db))) { - const char *var = GetVariable(pset.vars, "ECHO"); - - if (var && strncmp(var, "queries", strlen(var)) == 0) - puts(query); + fprintf(pset.queryFout, gettext("Asynchronous NOTIFY '%s' from backend with pid %d received.\n"), + notify->relname, notify->be_pid); + free(notify); + fflush(pset.queryFout); } +} - cancelConn = pset.db; - -#ifndef WIN32 - if (pset.timing) - gettimeofday(&before, NULL); - results = PQexec(pset.db, query); - if (pset.timing) - gettimeofday(&after, NULL); -#else - if (pset.timing) - _ftime(&before); - results = PQexec(pset.db, query); - if (pset.timing) - _ftime(&after); -#endif - - if (PQresultStatus(results) == PGRES_COPY_IN) - copy_in_state = true; - /* keep cancel connection for copy out state */ - if (PQresultStatus(results) != PGRES_COPY_OUT) - cancelConn = NULL; - if (results == NULL) - { - fputs(PQerrorMessage(pset.db), pset.queryFout); - success = false; - } - else - { - switch (PQresultStatus(results)) - { - case PGRES_TUPLES_OK: +/* + * PrintQueryTuples: assuming query result is OK, print its tuples + * + * Returns true if successful, false otherwise. + */ +static bool +PrintQueryTuples(const PGresult *results) +{ /* write output to \g argument, if any */ if (pset.gfname) { @@ -403,8 +469,7 @@ SendQuery(const char *query) { pset.queryFout = queryFout_copy; pset.queryFoutPipe = queryFoutPipe_copy; - success = false; - break; + return false; } printQuery(results, &pset.popt, pset.queryFout); @@ -417,14 +482,38 @@ SendQuery(const char *query) free(pset.gfname); pset.gfname = NULL; - - success = true; } else { printQuery(results, &pset.popt, pset.queryFout); - success = true; } + + return true; +} + + + +/* + * PrintQueryResults: analyze query results and print them out + * + * Note: Utility function for use by SendQuery() only. + * + * Returns true if the query executed successfully, false otherwise. + */ +static bool +PrintQueryResults(PGresult *results, + const TimevalStruct *before, + const TimevalStruct *after) +{ + bool success = false; + + if (!results) + return false; + + switch (PQresultStatus(results)) + { + case PGRES_TUPLES_OK: + success = PrintQueryTuples(results); break; case PGRES_EMPTY_QUERY: success = true; @@ -453,64 +542,80 @@ SendQuery(const char *query) pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL); break; - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - case PGRES_BAD_RESPONSE: - success = false; - psql_error("%s", PQerrorMessage(pset.db)); + default: break; } fflush(pset.queryFout); - if (PQstatus(pset.db) == CONNECTION_BAD) - { - if (!pset.cur_cmd_interactive) + if (!CheckConnection()) return false; + + /* Possible microtiming output */ + if (pset.timing && success) + printf(gettext("Time: %.2f ms\n"), DIFF_MSEC(after, before)); + + return success; +} + + + +/* + * SendQuery: send the query string to the backend + * (and print out results) + * + * Note: This is the "front door" way to send a query. That is, use it to + * send queries actually entered by the user. These queries will be subject to + * single step mode. + * To send "back door" queries (generated by slash commands, etc.) in a + * controlled way, use PSQLexec(). + * + * Returns true if the query executed successfully, false otherwise. + */ +bool +SendQuery(const char *query) +{ + PGresult *results; + TimevalStruct before, after; + bool OK; + + if (!pset.db) { - psql_error("connection to server was lost\n"); - exit(EXIT_BADCONN); + psql_error("You are currently not connected to a database.\n"); + return false; } - fputs(gettext("The connection to the server was lost. Attempting reset: "), stderr); - PQreset(pset.db); - if (PQstatus(pset.db) == CONNECTION_BAD) + + if (GetVariableBool(pset.vars, "SINGLESTEP")) { - fputs(gettext("Failed.\n"), stderr); - PQfinish(pset.db); - PQclear(results); - pset.db = NULL; - 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); + char buf[3]; + + printf(gettext("***(Single step mode: Verify query)*********************************************\n" + "%s\n" + "***(press return to proceed or enter x and return to cancel)********************\n"), + query); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) != NULL) + if (buf[0] == 'x') return false; } else - fputs(gettext("Succeeded.\n"), stderr); - } - - /* check for asynchronous notification returns */ - while ((notify = PQnotifies(pset.db)) != NULL) { - fprintf(pset.queryFout, gettext("Asynchronous NOTIFY '%s' from backend with pid %d received.\n"), - notify->relname, notify->be_pid); - free(notify); - fflush(pset.queryFout); - } + const char *var = GetVariable(pset.vars, "ECHO"); - if (results) - PQclear(results); + if (var && strncmp(var, "queries", strlen(var)) == 0) + puts(query); } - /* Possible microtiming output */ - if (pset.timing && success) -#ifndef WIN32 - printf(gettext("Time: %.2f ms\n"), - ((after.tv_sec - before.tv_sec) * 1000000.0 + after.tv_usec - before.tv_usec) / 1000.0); -#else - printf(gettext("Time: %.2f ms\n"), - ((after.time - before.time) * 1000.0 + after.millitm - before.millitm)); -#endif + SetCancelConn(); - return success; + if (pset.timing) + GETTIMEOFDAY(&before); + results = PQexec(pset.db, query); + if (pset.timing) + GETTIMEOFDAY(&after); + + OK = (AcceptResult(results) && PrintQueryResults(results, &before, &after)); + PQclear(results); + + PrintNotifications(); + return OK; } diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index fdfcdc05eb..b15cdc6c79 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.22 2003/03/18 22:15:44 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/common.h,v 1.23 2003/03/20 06:00:12 momjian Exp $ */ #ifndef COMMON_H #define COMMON_H @@ -29,6 +29,8 @@ extern char *simple_prompt(const char *prompt, int maxlen, bool echo); extern volatile bool cancel_pressed; extern PGconn *cancelConn; +extern void ResetCancelConn(void); + #ifndef WIN32 extern void handle_sigint(SIGNAL_ARGS); #endif /* not WIN32 */ diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index b70519be15..00664ea05d 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.28 2002/10/19 00:22:14 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/copy.c,v 1.29 2003/03/20 06:00:12 momjian Exp $ */ #include "postgres_fe.h" #include "copy.h" @@ -446,8 +446,6 @@ handleCopyOut(PGconn *conn, FILE *copystream) char copybuf[COPYBUFSIZ]; int ret; - assert(cancelConn); - while (!copydone) { ret = PQgetline(conn, copybuf, COPYBUFSIZ); @@ -476,7 +474,7 @@ handleCopyOut(PGconn *conn, FILE *copystream) } fflush(copystream); ret = !PQendcopy(conn); - cancelConn = NULL; + ResetCancelConn(); return ret; } diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index 6bf18c757c..24f6a9b40b 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/input.c,v 1.21 2002/09/06 02:33:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/input.c,v 1.22 2003/03/20 06:00:12 momjian Exp $ */ #include "postgres_fe.h" #include "input.h" @@ -20,6 +20,15 @@ #ifdef USE_READLINE static bool useReadline; static bool useHistory; + +enum histcontrol +{ + hctl_none = 0, + hctl_ignorespace = 1, + hctl_ignoredups = 2, + hctl_ignoreboth = hctl_ignorespace | hctl_ignoredups +}; + #endif #ifdef HAVE_ATEXIT @@ -33,6 +42,35 @@ static void finishInput(int, void *); #define PSQLHISTORY ".psql_history" +#ifdef USE_READLINE +static enum histcontrol +GetHistControlConfig(void) +{ + enum histcontrol HC; + const char *var; + + var = GetVariable(pset.vars, "HISTCONTROL"); + + if (!var) HC = hctl_none; + else if (strcmp(var, "ignorespace") == 0) HC = hctl_ignorespace; + else if (strcmp(var, "ignoredups") == 0) HC = hctl_ignoredups; + else if (strcmp(var, "ignoreboth") == 0) HC = hctl_ignoreboth; + else HC = hctl_none; + + return HC; +} +#endif + + +static char * +gets_basic(const char prompt[]) +{ + fputs(prompt, stdout); + fflush(stdout); + return gets_fromFile(stdin); +} + + /* * gets_interactive() * @@ -40,45 +78,41 @@ static void finishInput(int, void *); * The result is malloced. */ char * -gets_interactive(char *prompt) +gets_interactive(const char *prompt) { +#ifdef USE_READLINE char *s; -#ifdef USE_READLINE - const char *var; static char *prev_hist = NULL; -#endif -#ifdef USE_READLINE if (useReadline) s = readline(prompt); else + s = gets_basic(prompt); + + if (useHistory && s && s[0]) { -#endif - fputs(prompt, stdout); - fflush(stdout); - s = gets_fromFile(stdin); -#ifdef USE_READLINE - } -#endif + enum histcontrol HC; -#ifdef USE_READLINE - if (useHistory && s && s[0] != '\0') + HC = GetHistControlConfig(); + + if (((HC & hctl_ignorespace) && s[0] == ' ') || + ((HC & hctl_ignoredups) && prev_hist && strcmp(s, prev_hist) == 0)) { - var = GetVariable(pset.vars, "HISTCONTROL"); - if (!var || (var - && !((strcmp(var, "ignorespace") == 0 || strcmp(var, "ignoreboth") == 0) && s[0] == ' ') - && !((strcmp(var, "ignoredups") == 0 || strcmp(var, "ignoreboth") == 0) && prev_hist && strcmp(s, prev_hist) == 0) - )) + /* Ignore this line as far as history is concerned */ + } + else { free(prev_hist); prev_hist = strdup(s); add_history(s); } } -#endif return s; +#else + return gets_basic(prompt); +#endif } @@ -126,17 +160,12 @@ void initializeInput(int flags) { #ifdef USE_READLINE - if (flags == 1) + if (flags & 1) { + const char *home; + useReadline = true; initialize_readline(); - } -#endif - -#ifdef USE_READLINE - if (flags == 1) - { - const char *home; useHistory = true; SetVariable(pset.vars, "HISTSIZE", "500"); @@ -172,18 +201,14 @@ saveHistory(char *fname) #ifdef USE_READLINE if (useHistory && fname) { - if (write_history(fname) != 0) - { - psql_error("could not save history to %s: %s\n", fname, strerror(errno)); - return false; - } + if (write_history(fname) == 0) return true; + + psql_error("could not save history to %s: %s\n", fname, strerror(errno)); } - else - return false; -#else - return false; #endif + + return false; } diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h index 4ed51f59ca..541d80b055 100644 --- a/src/bin/psql/input.h +++ b/src/bin/psql/input.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/input.h,v 1.18 2003/02/19 04:04:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/input.h,v 1.19 2003/03/20 06:00:12 momjian Exp $ */ #ifndef INPUT_H #define INPUT_H @@ -32,7 +32,8 @@ #endif #endif -char *gets_interactive(char *prompt); + +char *gets_interactive(const char *prompt); char *gets_fromFile(FILE *source); void initializeInput(int flags); diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index 1f84d50796..e9ed1a621e 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/mainloop.c,v 1.51 2002/10/12 23:09:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/mainloop.c,v 1.52 2003/03/20 06:00:12 momjian Exp $ */ #include "postgres_fe.h" #include "mainloop.h" @@ -92,8 +92,6 @@ MainLoop(FILE *source) /* main loop to get queries and execute them */ while (1) { -#ifndef WIN32 - /* * Welcome code for Control-C */ @@ -113,6 +111,7 @@ MainLoop(FILE *source) cancel_pressed = false; } +#ifndef WIN32 if (sigsetjmp(main_loop_jmp, 1) != 0) { /* got here with longjmp */ -- GitLab