|
7 | 7 | * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
8 | 8 | * Portions Copyright (c) 1994, Regents of the University of California
|
9 | 9 | *
|
10 |
| - * $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.25 2007/01/05 22:19:50 momjian Exp $ |
| 10 | + * $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.26 2007/04/09 18:21:22 mha Exp $ |
11 | 11 | *
|
12 | 12 | *-------------------------------------------------------------------------
|
13 | 13 | */
|
14 | 14 |
|
15 | 15 | #include "postgres_fe.h"
|
16 | 16 |
|
17 | 17 | #include <pwd.h>
|
| 18 | +#include <signal.h> |
18 | 19 | #include <unistd.h>
|
19 | 20 |
|
20 | 21 | #include "common.h"
|
| 22 | +#include "libpq/pqsignal.h" |
| 23 | + |
| 24 | +static void SetCancelConn(PGconn *conn); |
| 25 | +static void ResetCancelConn(void); |
21 | 26 |
|
22 | 27 | #ifndef HAVE_INT_OPTRESET
|
23 | 28 | int optreset;
|
24 | 29 | #endif
|
25 | 30 |
|
| 31 | +static PGcancel *volatile cancelConn = NULL; |
| 32 | +#ifdef WIN32 |
| 33 | +static CRITICAL_SECTION cancelConnLock; |
| 34 | +#endif |
26 | 35 |
|
27 | 36 | /*
|
28 | 37 | * Returns the current user name.
|
@@ -194,6 +203,33 @@ executeCommand(PGconn *conn, const char *query,
|
194 | 203 | }
|
195 | 204 |
|
196 | 205 |
|
| 206 | +/* |
| 207 | + * As above for a SQL maintenance command (returns command success). |
| 208 | + * Command is executed with a cancel handler set, so Ctrl-C can |
| 209 | + * interrupt it. |
| 210 | + */ |
| 211 | +bool |
| 212 | +executeMaintenanceCommand(PGconn *conn, const char *query, bool echo) |
| 213 | +{ |
| 214 | + PGresult *res; |
| 215 | + bool r; |
| 216 | + |
| 217 | + if (echo) |
| 218 | + printf("%s\n", query); |
| 219 | + |
| 220 | + SetCancelConn(conn); |
| 221 | + res = PQexec(conn, query); |
| 222 | + ResetCancelConn(); |
| 223 | + |
| 224 | + r = (res && PQresultStatus(res) == PGRES_COMMAND_OK); |
| 225 | + |
| 226 | + if (res) |
| 227 | + PQclear(res); |
| 228 | + |
| 229 | + return r; |
| 230 | +} |
| 231 | + |
| 232 | + |
197 | 233 | /*
|
198 | 234 | * Check yes/no answer in a localized way. 1=yes, 0=no, -1=neither.
|
199 | 235 | */
|
@@ -237,3 +273,135 @@ yesno_prompt(const char *question)
|
237 | 273 | _(PG_YESLETTER), _(PG_NOLETTER));
|
238 | 274 | }
|
239 | 275 | }
|
| 276 | + |
| 277 | + |
| 278 | +/* |
| 279 | + * SetCancelConn |
| 280 | + * |
| 281 | + * Set cancelConn to point to the current database connection. |
| 282 | + */ |
| 283 | +static void |
| 284 | +SetCancelConn(PGconn *conn) |
| 285 | +{ |
| 286 | + PGcancel *oldCancelConn; |
| 287 | + |
| 288 | +#ifdef WIN32 |
| 289 | + EnterCriticalSection(&cancelConnLock); |
| 290 | +#endif |
| 291 | + |
| 292 | + /* Free the old one if we have one */ |
| 293 | + oldCancelConn = cancelConn; |
| 294 | + |
| 295 | + /* be sure handle_sigint doesn't use pointer while freeing */ |
| 296 | + cancelConn = NULL; |
| 297 | + |
| 298 | + if (oldCancelConn != NULL) |
| 299 | + PQfreeCancel(oldCancelConn); |
| 300 | + |
| 301 | + cancelConn = PQgetCancel(conn); |
| 302 | + |
| 303 | +#ifdef WIN32 |
| 304 | + LeaveCriticalSection(&cancelConnLock); |
| 305 | +#endif |
| 306 | +} |
| 307 | + |
| 308 | +/* |
| 309 | + * ResetCancelConn |
| 310 | + * |
| 311 | + * Free the current cancel connection, if any, and set to NULL. |
| 312 | + */ |
| 313 | +static void |
| 314 | +ResetCancelConn(void) |
| 315 | +{ |
| 316 | + PGcancel *oldCancelConn; |
| 317 | + |
| 318 | +#ifdef WIN32 |
| 319 | + EnterCriticalSection(&cancelConnLock); |
| 320 | +#endif |
| 321 | + |
| 322 | + oldCancelConn = cancelConn; |
| 323 | + |
| 324 | + /* be sure handle_sigint doesn't use pointer while freeing */ |
| 325 | + cancelConn = NULL; |
| 326 | + |
| 327 | + if (oldCancelConn != NULL) |
| 328 | + PQfreeCancel(oldCancelConn); |
| 329 | + |
| 330 | +#ifdef WIN32 |
| 331 | + LeaveCriticalSection(&cancelConnLock); |
| 332 | +#endif |
| 333 | +} |
| 334 | + |
| 335 | +#ifndef WIN32 |
| 336 | +/* |
| 337 | + * Handle interrupt signals by cancelling the current command, |
| 338 | + * if it's being executed through executeMaintenanceCommand(), |
| 339 | + * and thus has a cancelConn set. |
| 340 | + */ |
| 341 | +static void |
| 342 | +handle_sigint(SIGNAL_ARGS) |
| 343 | +{ |
| 344 | + int save_errno = errno; |
| 345 | + char errbuf[256]; |
| 346 | + |
| 347 | + /* Send QueryCancel if we are processing a database query */ |
| 348 | + if (cancelConn != NULL) |
| 349 | + { |
| 350 | + if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) |
| 351 | + fprintf(stderr, _("Cancel request sent\n")); |
| 352 | + else |
| 353 | + fprintf(stderr, _("Could not send cancel request: %s\n"), errbuf); |
| 354 | + } |
| 355 | + |
| 356 | + errno = save_errno; /* just in case the write changed it */ |
| 357 | +} |
| 358 | + |
| 359 | +void |
| 360 | +setup_cancel_handler(void) |
| 361 | +{ |
| 362 | + pqsignal(SIGINT, handle_sigint); |
| 363 | +} |
| 364 | + |
| 365 | +#else /* WIN32 */ |
| 366 | + |
| 367 | +/* |
| 368 | + * Console control handler for Win32. Note that the control handler will |
| 369 | + * execute on a *different thread* than the main one, so we need to do |
| 370 | + * proper locking around those structures. |
| 371 | + */ |
| 372 | +static BOOL WINAPI |
| 373 | +consoleHandler(DWORD dwCtrlType) |
| 374 | +{ |
| 375 | + char errbuf[256]; |
| 376 | + |
| 377 | + if (dwCtrlType == CTRL_C_EVENT || |
| 378 | + dwCtrlType == CTRL_BREAK_EVENT) |
| 379 | + { |
| 380 | + /* Send QueryCancel if we are processing a database query */ |
| 381 | + EnterCriticalSection(&cancelConnLock); |
| 382 | + if (cancelConn != NULL) |
| 383 | + { |
| 384 | + if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) |
| 385 | + fprintf(stderr, _("Cancel request sent\n")); |
| 386 | + else |
| 387 | + fprintf(stderr, _("Could not send cancel request: %s"), errbuf); |
| 388 | + } |
| 389 | + LeaveCriticalSection(&cancelConnLock); |
| 390 | + |
| 391 | + return TRUE; |
| 392 | + } |
| 393 | + else |
| 394 | + /* Return FALSE for any signals not being handled */ |
| 395 | + return FALSE; |
| 396 | +} |
| 397 | + |
| 398 | +void |
| 399 | +setup_cancel_handler(void) |
| 400 | +{ |
| 401 | + InitializeCriticalSection(&cancelConnLock); |
| 402 | + |
| 403 | + SetConsoleCtrlHandler(consoleHandler, TRUE); |
| 404 | +} |
| 405 | + |
| 406 | +#endif /* WIN32 */ |
| 407 | + |
0 commit comments