diff -B -b -c -r pgsql.orig/src/bin/psql/common.c pgsql.patched/src/bin/psql/common.c *** pgsql.orig/src/bin/psql/common.c 2003-02-22 16:30:41.000000000 +0100 --- pgsql.patched/src/bin/psql/common.c 2003-02-24 03:36:21.000000000 +0100 *************** *** 3,9 **** * * Copyright 2000 by PostgreSQL Global Development Group * ! * $Header: /projects/cvsroot/pgsql-server/src/bin/psql/common.c,v 1.55 2003/02/21 21:34:27 tgl Exp $ */ #include "postgres_fe.h" #include "common.h" --- 3,9 ---- * * Copyright 2000 by PostgreSQL Global Development Group * ! * $Header: /projects/cvsroot/pgsql-server/src/bin/psql/common.c,v 1.54 2003/02/19 03:54:39 momjian Exp $ */ #include "postgres_fe.h" #include "common.h" *************** *** 42,47 **** --- 42,67 ---- #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; /* *************** *** 118,127 **** /* Direct signals */ #ifndef WIN32 ! if (pset.queryFoutPipe) ! pqsignal(SIGPIPE, SIG_IGN); ! else ! pqsignal(SIGPIPE, SIG_DFL); #endif return status; --- 138,144 ---- /* Direct signals */ #ifndef WIN32 ! pqsignal(SIGPIPE, pset.queryFoutPipe ? SIG_IGN : SIG_DFL); #endif return status; *************** *** 173,180 **** * 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; #ifndef WIN32 --- 190,199 ---- * so. We use write() to print to stdout because it's better to use simple * facilities in a signal handler. */ ! static PGconn *volatile cancelConn = NULL; ! ! volatile bool cancel_pressed = false; ! #ifndef WIN32 *************** *** 206,211 **** --- 225,379 ---- #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; + } + + + /* + * CheckCancelConn + * + * See if cancelConn is set. This is asserted, though not otherwise used, by + * copy.c. + */ + bool CheckCancelConn(void) + { + return 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 * *************** *** 247,402 **** while ((newres = PQgetResult(pset.db)) != NULL) PQclear(newres); ! cancelConn = pset.db; ! if (PQsendQuery(pset.db, query)) { ! while ((newres = PQgetResult(pset.db)) != NULL) { rstatus = PQresultStatus(newres); ! if (ignore_command_ok && rstatus == PGRES_COMMAND_OK) { ! PQclear(newres); ! continue; ! } ! PQclear(res); res = newres; ! if (rstatus == PGRES_COPY_IN || ! rstatus == PGRES_COPY_OUT) ! break; } } - 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 { - 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); } ! return NULL; ! } } /* ! * 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) { - 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 ! { ! const char *var = GetVariable(pset.vars, "ECHO"); ! ! if (var && strncmp(var, "queries", strlen(var)) == 0) ! puts(query); } - 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: /* write output to \g argument, if any */ if (pset.gfname) { --- 415,482 ---- 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))) { rstatus = PQresultStatus(newres); ! if (!ignore_command_ok || rstatus != PGRES_COMMAND_OK) { ! PGresult *tempRes = res; res = newres; ! newres = tempRes; } + PQclear(newres); } ! if (!AcceptResult(res) && res) { PQclear(res); ! res = NULL; } ! return res; } /* ! * PrintNotifications: check for asynchronous notifications, and print them out * */ ! static void ! PrintNotifications(void) { PGnotify *notify; ! while ((notify = PQnotifies(pset.db))) { ! fprintf(pset.queryFout, gettext("Asynchronous NOTIFY '%s' from backend with pid %d received.\n"), ! notify->relname, notify->be_pid); ! free(notify); ! fflush(pset.queryFout); } + } ! /* ! * 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) { *************** *** 411,418 **** { pset.queryFout = queryFout_copy; pset.queryFoutPipe = queryFoutPipe_copy; ! success = false; ! break; } printQuery(results, &pset.popt, pset.queryFout); --- 491,497 ---- { pset.queryFout = queryFout_copy; pset.queryFoutPipe = queryFoutPipe_copy; ! return false; } printQuery(results, &pset.popt, pset.queryFout); *************** *** 425,438 **** free(pset.gfname); pset.gfname = NULL; - - success = true; } else { printQuery(results, &pset.popt, pset.queryFout); - success = true; } break; case PGRES_EMPTY_QUERY: success = true; --- 504,541 ---- free(pset.gfname); pset.gfname = NULL; } else { printQuery(results, &pset.popt, pset.queryFout); } + + 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; *************** *** 461,526 **** 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)); break; } fflush(pset.queryFout); ! 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); ! 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); 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); ! } ! if (results) ! PQclear(results); } ! /* 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 ! return success; } --- 564,645 ---- pset.cur_cmd_interactive ? get_prompt(PROMPT_COPY) : NULL); break; ! default: break; } fflush(pset.queryFout); ! 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("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 { ! const char *var = GetVariable(pset.vars, "ECHO"); ! if (var && strncmp(var, "queries", strlen(var)) == 0) ! puts(query); } ! SetCancelConn(); ! 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 -B -b -c -r pgsql.orig/src/bin/psql/common.h pgsql.patched/src/bin/psql/common.h *** pgsql.orig/src/bin/psql/common.h 2003-02-24 01:55:25.000000000 +0100 --- pgsql.patched/src/bin/psql/common.h 2003-02-24 03:25:18.000000000 +0100 *************** *** 26,35 **** extern char *simple_prompt(const char *prompt, int maxlen, bool echo); - extern volatile bool cancel_pressed; void ResetCancelConn(void); bool CheckCancelConn(void); #ifndef WIN32 extern void handle_sigint(SIGNAL_ARGS); #endif /* not WIN32 */ --- 26,36 ---- extern char *simple_prompt(const char *prompt, int maxlen, bool echo); void ResetCancelConn(void); bool CheckCancelConn(void); + extern volatile bool cancel_pressed; + #ifndef WIN32 extern void handle_sigint(SIGNAL_ARGS); #endif /* not WIN32 */ diff -B -b -c -r pgsql.orig/src/bin/psql/input.c pgsql.patched/src/bin/psql/input.c *** pgsql.orig/src/bin/psql/input.c 2003-02-22 16:24:27.000000000 +0100 --- pgsql.patched/src/bin/psql/input.c 2003-02-24 04:52:53.000000000 +0100 *************** *** 20,25 **** --- 20,34 ---- #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,38 **** --- 42,76 ---- #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,84 **** * The result is malloced. */ char * ! gets_interactive(char *prompt) { char *s; - #ifdef USE_READLINE - const char *var; static char *prev_hist = NULL; - #endif - #ifdef USE_READLINE if (useReadline) s = readline(prompt); else { ! #endif ! fputs(prompt, stdout); ! fflush(stdout); ! s = gets_fromFile(stdin); ! #ifdef USE_READLINE ! } ! #endif ! #ifdef USE_READLINE ! if (useHistory && s && s[0] != '\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) ! )) { free(prev_hist); prev_hist = strdup(s); add_history(s); } } - #endif return s; } --- 78,118 ---- * The result is malloced. */ char * ! gets_interactive(const char *prompt) { + #ifdef USE_READLINE char *s; static char *prev_hist = NULL; if (useReadline) s = readline(prompt); else + s = gets_basic(prompt); + + if (useHistory && s && s[0]) { ! enum histcontrol HC; ! HC = GetHistControlConfig(); ! ! if (((HC & hctl_ignorespace) && s[0] == ' ') || ! ((HC & hctl_ignoredups) && 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); } } return s; + #else + return gets_basic(prompt); + #endif } *************** *** 126,142 **** initializeInput(int flags) { #ifdef USE_READLINE ! if (flags == 1) { useReadline = true; initialize_readline(); - } - #endif - - #ifdef USE_READLINE - if (flags == 1) - { - const char *home; useHistory = true; SetVariable(pset.vars, "HISTSIZE", "500"); --- 160,171 ---- initializeInput(int flags) { #ifdef USE_READLINE ! if (flags & 1) { + const char *home; + useReadline = true; initialize_readline(); useHistory = true; SetVariable(pset.vars, "HISTSIZE", "500"); *************** *** 172,189 **** #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; ! } return true; } - else - return false; - #else - return false; #endif } --- 201,214 ---- #ifdef USE_READLINE if (useHistory && fname) { ! if (write_history(fname) == 0) return true; + + psql_error("could not save history to %s: %s\n", fname, strerror(errno)); } #endif + + return false; } diff -B -b -c -r pgsql.orig/src/bin/psql/input.h pgsql.patched/src/bin/psql/input.h *** pgsql.orig/src/bin/psql/input.h 2003-02-22 16:30:41.000000000 +0100 --- pgsql.patched/src/bin/psql/input.h 2003-02-24 04:33:57.000000000 +0100 *************** *** 32,38 **** #endif #endif ! char *gets_interactive(char *prompt); char *gets_fromFile(FILE *source); void initializeInput(int flags); --- 32,39 ---- #endif #endif ! ! char *gets_interactive(const char *prompt); char *gets_fromFile(FILE *source); void initializeInput(int flags); diff -B -b -c -r pgsql.orig/src/bin/psql/mainloop.c pgsql.patched/src/bin/psql/mainloop.c *** pgsql.orig/src/bin/psql/mainloop.c 2003-02-22 16:24:27.000000000 +0100 --- pgsql.patched/src/bin/psql/mainloop.c 2003-02-24 04:36:57.000000000 +0100 *************** *** 92,99 **** /* main loop to get queries and execute them */ while (1) { - #ifndef WIN32 - /* * Welcome code for Control-C */ --- 92,97 ---- *************** *** 113,118 **** --- 111,117 ---- cancel_pressed = false; } + #ifndef WIN32 if (sigsetjmp(main_loop_jmp, 1) != 0) { /* got here with longjmp */