From 6e09df9d26520ddbd82ab7f3afb9fc14e891c88e Mon Sep 17 00:00:00 2001 From: Magnus Hagander Date: Mon, 9 Apr 2007 18:21:22 +0000 Subject: [PATCH] Add cancel handlers so it's possible to Ctrl-C clusterdb, reindexdb and vacuumdb. ITAGAKI Takahiro, with minor fixes from me. --- src/bin/scripts/clusterdb.c | 14 +-- src/bin/scripts/common.c | 170 +++++++++++++++++++++++++++++++++++- src/bin/scripts/common.h | 7 +- src/bin/scripts/reindexdb.c | 22 ++--- src/bin/scripts/vacuumdb.c | 14 +-- 5 files changed, 188 insertions(+), 39 deletions(-) diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c index 193eaf781a..c077e26f43 100644 --- a/src/bin/scripts/clusterdb.c +++ b/src/bin/scripts/clusterdb.c @@ -4,7 +4,7 @@ * * Portions Copyright (c) 2002-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/scripts/clusterdb.c,v 1.16 2007/02/13 18:06:18 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/clusterdb.c,v 1.17 2007/04/09 18:21:22 mha Exp $ * *------------------------------------------------------------------------- */ @@ -111,6 +111,8 @@ main(int argc, char *argv[]) exit(1); } + setup_cancel_handler(); + if (alldb) { if (dbname) @@ -159,7 +161,6 @@ cluster_one_database(const char *dbname, const char *table, PQExpBufferData sql; PGconn *conn; - PGresult *result; initPQExpBuffer(&sql); @@ -169,12 +170,7 @@ cluster_one_database(const char *dbname, const char *table, appendPQExpBuffer(&sql, ";\n"); conn = connectDatabase(dbname, host, port, username, password, progname); - - if (echo) - printf("%s", sql.data); - result = PQexec(conn, sql.data); - - if (PQresultStatus(result) != PGRES_COMMAND_OK) + if (!executeMaintenanceCommand(conn, sql.data, echo)) { if (table) fprintf(stderr, _("%s: clustering of table \"%s\" in database \"%s\" failed: %s"), @@ -185,8 +181,6 @@ cluster_one_database(const char *dbname, const char *table, PQfinish(conn); exit(1); } - - PQclear(result); PQfinish(conn); termPQExpBuffer(&sql); diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c index b12cba2e8b..dfe9a52be4 100644 --- a/src/bin/scripts/common.c +++ b/src/bin/scripts/common.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.25 2007/01/05 22:19:50 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.26 2007/04/09 18:21:22 mha Exp $ * *------------------------------------------------------------------------- */ @@ -15,14 +15,23 @@ #include "postgres_fe.h" #include +#include #include #include "common.h" +#include "libpq/pqsignal.h" + +static void SetCancelConn(PGconn *conn); +static void ResetCancelConn(void); #ifndef HAVE_INT_OPTRESET int optreset; #endif +static PGcancel *volatile cancelConn = NULL; +#ifdef WIN32 +static CRITICAL_SECTION cancelConnLock; +#endif /* * Returns the current user name. @@ -194,6 +203,33 @@ executeCommand(PGconn *conn, const char *query, } +/* + * As above for a SQL maintenance command (returns command success). + * Command is executed with a cancel handler set, so Ctrl-C can + * interrupt it. + */ +bool +executeMaintenanceCommand(PGconn *conn, const char *query, bool echo) +{ + PGresult *res; + bool r; + + if (echo) + printf("%s\n", query); + + SetCancelConn(conn); + res = PQexec(conn, query); + ResetCancelConn(); + + r = (res && PQresultStatus(res) == PGRES_COMMAND_OK); + + if (res) + PQclear(res); + + return r; +} + + /* * Check yes/no answer in a localized way. 1=yes, 0=no, -1=neither. */ @@ -237,3 +273,135 @@ yesno_prompt(const char *question) _(PG_YESLETTER), _(PG_NOLETTER)); } } + + +/* + * SetCancelConn + * + * Set cancelConn to point to the current database connection. + */ +static void +SetCancelConn(PGconn *conn) +{ + PGcancel *oldCancelConn; + +#ifdef WIN32 + EnterCriticalSection(&cancelConnLock); +#endif + + /* Free the old one if we have one */ + oldCancelConn = cancelConn; + + /* be sure handle_sigint doesn't use pointer while freeing */ + cancelConn = NULL; + + if (oldCancelConn != NULL) + PQfreeCancel(oldCancelConn); + + cancelConn = PQgetCancel(conn); + +#ifdef WIN32 + LeaveCriticalSection(&cancelConnLock); +#endif +} + +/* + * ResetCancelConn + * + * Free the current cancel connection, if any, and set to NULL. + */ +static void +ResetCancelConn(void) +{ + PGcancel *oldCancelConn; + +#ifdef WIN32 + EnterCriticalSection(&cancelConnLock); +#endif + + oldCancelConn = cancelConn; + + /* be sure handle_sigint doesn't use pointer while freeing */ + cancelConn = NULL; + + if (oldCancelConn != NULL) + PQfreeCancel(oldCancelConn); + +#ifdef WIN32 + LeaveCriticalSection(&cancelConnLock); +#endif +} + +#ifndef WIN32 +/* + * Handle interrupt signals by cancelling the current command, + * if it's being executed through executeMaintenanceCommand(), + * and thus has a cancelConn set. + */ +static void +handle_sigint(SIGNAL_ARGS) +{ + int save_errno = errno; + char errbuf[256]; + + /* Send QueryCancel if we are processing a database query */ + if (cancelConn != NULL) + { + if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) + fprintf(stderr, _("Cancel request sent\n")); + else + fprintf(stderr, _("Could not send cancel request: %s\n"), errbuf); + } + + errno = save_errno; /* just in case the write changed it */ +} + +void +setup_cancel_handler(void) +{ + pqsignal(SIGINT, handle_sigint); +} + +#else /* WIN32 */ + +/* + * Console control handler for Win32. Note that the control handler will + * execute on a *different thread* than the main one, so we need to do + * proper locking around those structures. + */ +static BOOL WINAPI +consoleHandler(DWORD dwCtrlType) +{ + char errbuf[256]; + + if (dwCtrlType == CTRL_C_EVENT || + dwCtrlType == CTRL_BREAK_EVENT) + { + /* Send QueryCancel if we are processing a database query */ + EnterCriticalSection(&cancelConnLock); + if (cancelConn != NULL) + { + if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) + fprintf(stderr, _("Cancel request sent\n")); + else + fprintf(stderr, _("Could not send cancel request: %s"), errbuf); + } + LeaveCriticalSection(&cancelConnLock); + + return TRUE; + } + else + /* Return FALSE for any signals not being handled */ + return FALSE; +} + +void +setup_cancel_handler(void) +{ + InitializeCriticalSection(&cancelConnLock); + + SetConsoleCtrlHandler(consoleHandler, TRUE); +} + +#endif /* WIN32 */ + diff --git a/src/bin/scripts/common.h b/src/bin/scripts/common.h index c2ecf1a663..9f59223047 100644 --- a/src/bin/scripts/common.h +++ b/src/bin/scripts/common.h @@ -4,7 +4,7 @@ * * Copyright (c) 2003-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/scripts/common.h,v 1.16 2007/01/05 22:19:50 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/common.h,v 1.17 2007/04/09 18:21:22 mha Exp $ */ #ifndef COMMON_H #define COMMON_H @@ -35,6 +35,11 @@ extern PGresult *executeQuery(PGconn *conn, const char *query, extern void executeCommand(PGconn *conn, const char *query, const char *progname, bool echo); +extern bool executeMaintenanceCommand(PGconn *conn, const char *query, + bool echo); + extern bool yesno_prompt(const char *question); +extern void setup_cancel_handler(void); + #endif /* COMMON_H */ diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index 057fbd876e..34dd72ff4b 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -4,7 +4,7 @@ * * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/scripts/reindexdb.c,v 1.9 2007/02/13 18:06:18 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/reindexdb.c,v 1.10 2007/04/09 18:21:22 mha Exp $ * *------------------------------------------------------------------------- */ @@ -126,6 +126,8 @@ main(int argc, char *argv[]) exit(1); } + setup_cancel_handler(); + if (alldb) { if (dbname) @@ -214,7 +216,6 @@ reindex_one_database(const char *name, const char *dbname, const char *type, PQExpBufferData sql; PGconn *conn; - PGresult *result; initPQExpBuffer(&sql); @@ -229,11 +230,7 @@ reindex_one_database(const char *name, const char *dbname, const char *type, conn = connectDatabase(dbname, host, port, username, password, progname); - if (echo) - printf("%s", sql.data); - result = PQexec(conn, sql.data); - - if (PQresultStatus(result) != PGRES_COMMAND_OK) + if (!executeMaintenanceCommand(conn, sql.data, echo)) { if (strcmp(type, "TABLE") == 0) fprintf(stderr, _("%s: reindexing of table \"%s\" in database \"%s\" failed: %s"), @@ -248,7 +245,6 @@ reindex_one_database(const char *name, const char *dbname, const char *type, exit(1); } - PQclear(result); PQfinish(conn); termPQExpBuffer(&sql); @@ -294,27 +290,19 @@ reindex_system_catalogs(const char *dbname, const char *host, const char *port, PQExpBufferData sql; PGconn *conn; - PGresult *result; initPQExpBuffer(&sql); appendPQExpBuffer(&sql, "REINDEX SYSTEM %s;\n", dbname); conn = connectDatabase(dbname, host, port, username, password, progname); - - if (echo) - printf("%s", sql.data); - result = PQexec(conn, sql.data); - - if (PQresultStatus(result) != PGRES_COMMAND_OK) + if (!executeMaintenanceCommand(conn, sql.data, echo)) { fprintf(stderr, _("%s: reindexing of system catalogs failed: %s"), progname, PQerrorMessage(conn)); PQfinish(conn); exit(1); } - - PQclear(result); PQfinish(conn); termPQExpBuffer(&sql); diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index aaa897b747..b767133361 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/bin/scripts/vacuumdb.c,v 1.16 2007/02/13 17:39:39 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/scripts/vacuumdb.c,v 1.17 2007/04/09 18:21:22 mha Exp $ * *------------------------------------------------------------------------- */ @@ -128,6 +128,8 @@ main(int argc, char *argv[]) exit(1); } + setup_cancel_handler(); + if (alldb) { if (dbname) @@ -178,7 +180,6 @@ vacuum_one_database(const char *dbname, bool full, bool verbose, bool analyze, PQExpBufferData sql; PGconn *conn; - PGresult *result; initPQExpBuffer(&sql); @@ -194,12 +195,7 @@ vacuum_one_database(const char *dbname, bool full, bool verbose, bool analyze, appendPQExpBuffer(&sql, ";\n"); conn = connectDatabase(dbname, host, port, username, password, progname); - - if (echo) - printf("%s", sql.data); - result = PQexec(conn, sql.data); - - if (PQresultStatus(result) != PGRES_COMMAND_OK) + if (!executeMaintenanceCommand(conn, sql.data, echo)) { if (table) fprintf(stderr, _("%s: vacuuming of table \"%s\" in database \"%s\" failed: %s"), @@ -210,8 +206,6 @@ vacuum_one_database(const char *dbname, bool full, bool verbose, bool analyze, PQfinish(conn); exit(1); } - - PQclear(result); PQfinish(conn); termPQExpBuffer(&sql);