Index: src/bin/psql/common.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/common.c,v retrieving revision 1.108 diff -c -r1.108 common.c *** src/bin/psql/common.c 15 Oct 2005 02:49:40 -0000 1.108 --- src/bin/psql/common.c 22 Oct 2005 19:11:41 -0000 *************** *** 230,240 **** --- 230,267 ---- * for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required * to protect the PGcancel structure against being changed while the other * thread is using it. + * + * * * + * About ctrlc_abort_jmp and ctrlc_abort_active: + * + * In the past the SIGINT handler, when it couldn't find a query to cancel, + * would simply longjmp() back to the main loop. Very simple but + * unfortunatly it leaked memory like crazy and file descriptors too if you + * were using a pager. Hence, the handler has been changed to just set a + * flag in those cases so the code would notice and gracefully quit. + * + * However, PostgreSQL uses signals with BSD semantics, which means the + * system calls get restarted across a signal. Hence when sitting at the + * prompt pressing Ctrl-C appeared to do nothing because the kernel would + * keep restarting the read(). The only way to avoid this is to have the + * signal handler longjmp(). + * + * Hence the rules are as follows: If you are reading from a descriptor + * which may block indefinitly and you want to be able to respond to Ctrl-C + * you should setup with sigsetjmp() the jump buffer ctrlc_abort_jmp. And + * when you are about to read something set ctrlc_abort_active to true. + * After the read set it back to false. If while the flag is true the user + * presses Ctrl-C, control will pass to the sigsetjmp() block which allows + * you to clean up gracefully and return. See gets_basic() and + * gets_readline() in input.c for examples. */ static PGcancel *cancelConn = NULL; #ifdef WIN32 static CRITICAL_SECTION cancelConnLock; + #else + extern sigjmp_buf ctrlc_abort_jmp; + extern volatile bool ctrlc_abort_active; #endif volatile bool cancel_pressed = false; *************** *** 254,264 **** if (prompt_state) return; ! if (cancelConn == NULL) ! siglongjmp(main_loop_jmp, 1); ! cancel_pressed = true; if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) write_stderr("Cancel request sent\n"); else --- 281,309 ---- if (prompt_state) return; ! /* PostgreSQL uses BSD signal semantics, which means that system ! * calls restart across signals. Hence just setting a flag here ! * won't do squat if we're waiting for user input. The *only* way to ! * abort a read/write is to siglongjump to somewhere up the stack. ! * ! * To this end, the input routines setup the jump buffer and a flag ! * to indicate when it is valid. If this flag is set we jump out. ! * Otherwise, we just set the flag and psql will notice in due ! * course */ ! ! if( ctrlc_abort_active ) ! { ! ctrlc_abort_active = false; ! siglongjmp( ctrlc_abort_jmp, 1 ); ! } ! ! /* We only now set the cancel flag, since in the previous cases the ! * program deals with it directly. */ cancel_pressed = true; + if (cancelConn == NULL) /* Just return if no active query, we've set the flag */ + return; + if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) write_stderr("Cancel request sent\n"); else Index: src/bin/psql/input.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/input.c,v retrieving revision 1.46 diff -c -r1.46 input.c *** src/bin/psql/input.c 15 Oct 2005 02:49:40 -0000 1.46 --- src/bin/psql/input.c 22 Oct 2005 19:11:41 -0000 *************** *** 14,19 **** --- 14,25 ---- #include "common.h" #ifndef WIN32 + #include + sigjmp_buf ctrlc_abort_jmp; + volatile bool ctrlc_abort_active; + #endif + + #ifndef WIN32 #define PSQLHISTORY ".psql_history" #else #define PSQLHISTORY "psql_history" *************** *** 77,105 **** return gets_fromFile(stdin); } - - /* - * gets_interactive() - * - * Gets a line of interactive input, using readline of desired. - * The result is malloc'ed. - */ - char * - gets_interactive(const char *prompt) - { #ifdef USE_READLINE char *s; ! static char *prev_hist = NULL; ! if (useReadline) ! /* On some platforms, readline is declared as readline(char *) */ ! s = readline((char *) prompt); ! else ! s = gets_basic(prompt); if (useHistory && s && s[0]) { enum histcontrol HC; HC = GetHistControlConfig(); --- 83,114 ---- return gets_fromFile(stdin); } #ifdef USE_READLINE + static char * + gets_readline(const char prompt[]) + { char *s; ! #ifndef WIN32 ! /* Setup Ctrl-C handler, see common.c for details */ ! if (sigsetjmp(ctrlc_abort_jmp, 1) != 0) ! { ! fputc( '\n', stdout ); ! return strdup("\\r"); /* Clear query buffer */ ! } ! ctrlc_abort_active = true; ! #endif ! /* On some platforms, readline is declared as readline(char *) */ ! s = readline((char *) prompt); ! #ifndef WIN32 ! ctrlc_abort_active = false; ! #endif if (useHistory && s && s[0]) { + static char *prev_hist = NULL; + enum histcontrol HC; HC = GetHistControlConfig(); *************** *** 118,128 **** } return s; - #else - return gets_basic(prompt); - #endif } /* --- 127,150 ---- } return s; } + #endif + /* + * gets_interactive() + * + * Gets a line of interactive input, using readline of desired. + * The result is malloc'ed. + */ + char * + gets_interactive(const char *prompt) + { + #ifdef USE_READLINE + if( useReadline ) + return gets_readline( prompt ); + #endif + return gets_basic(prompt); + } /* *************** *** 138,145 **** initPQExpBuffer(&buffer); ! while (fgets(line, sizeof(line), source) != NULL) { appendPQExpBufferStr(&buffer, line); if (buffer.data[buffer.len - 1] == '\n') { --- 160,196 ---- initPQExpBuffer(&buffer); ! #ifndef WIN32 ! /* Setup Ctrl-C handler, see common.c for details */ ! if (sigsetjmp(ctrlc_abort_jmp, 1) != 0) ! { ! fputc( '\n', stdout ); ! termPQExpBuffer(&buffer); ! return strdup("\\r"); /* Clear query buffer */ ! } ! #endif ! ! /* The reason for this funny looking loop is to handle Ctrl-C ! * properly. See common.c for full details, but the gist of it is ! * that the user pressing Ctrl-C will not make the fgets return. ! * Hence, the ctrlc_abort_active flag is set so that *only* in the ! * case that we are waiting in fgets does the little bit of code ! * above get executed. Otherwise you cause a race condition because ! * appendPQExpBufferStr is not safe to longjmp out of. */ ! ! while( !cancel_pressed ) { + char *s; + #ifndef WIN32 + ctrlc_abort_active = true; + #endif + s = fgets(line, sizeof(line), source); + #ifndef WIN32 + ctrlc_abort_active = false; + #endif + if (s == NULL) + break; + appendPQExpBufferStr(&buffer, line); if (buffer.data[buffer.len - 1] == '\n') { Index: src/bin/psql/mainloop.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/mainloop.c,v retrieving revision 1.68 diff -c -r1.68 mainloop.c *** src/bin/psql/mainloop.c 15 Oct 2005 02:49:40 -0000 1.68 --- src/bin/psql/mainloop.c 22 Oct 2005 19:11:41 -0000 *************** *** 17,27 **** #include "psqlscan.h" #include "settings.h" - #ifndef WIN32 - #include - sigjmp_buf main_loop_jmp; - #endif - /* * Main processing loop for reading lines of input --- 17,22 ---- *************** *** 84,124 **** { /* * You get here if you stopped a script with Ctrl-C and a ! * query cancel was issued. In that case we don't do the ! * longjmp, so the query routine can finish nicely. */ successResult = EXIT_USER; break; } ! cancel_pressed = false; } - #ifndef WIN32 - if (sigsetjmp(main_loop_jmp, 1) != 0) - { - /* got here with longjmp */ - - /* reset parsing state */ - resetPQExpBuffer(query_buf); - psql_scan_finish(scan_state); - psql_scan_reset(scan_state); - count_eof = 0; - slashCmdStatus = CMD_UNKNOWN; - prompt_status = PROMPT_READY; - - if (pset.cur_cmd_interactive) - putc('\n', stdout); - else - { - successResult = EXIT_USER; - break; - } - } - /* ! * establish the control-C handler only after main_loop_jmp is ready */ pqsignal(SIGINT, handle_sigint); /* control-C => cancel */ #else /* WIN32 */ setup_cancel_handler(); --- 79,98 ---- { /* * You get here if you stopped a script with Ctrl-C and a ! * query cancel was issued. */ successResult = EXIT_USER; break; } ! /* Only print this when interactive */ ! fprintf( stderr, "Interrupted\n" ); cancel_pressed = false; } /* ! * establish the control-C handler */ + #ifndef WIN32 pqsignal(SIGINT, handle_sigint); /* control-C => cancel */ #else /* WIN32 */ setup_cancel_handler(); *************** *** 286,291 **** --- 260,268 ---- if (scan_result == PSCAN_INCOMPLETE || scan_result == PSCAN_EOL) break; + + if (cancel_pressed) + break; } psql_scan_finish(scan_state); *************** *** 321,336 **** successResult = EXIT_BADCONN; } - /* - * Reset SIGINT handler because main_loop_jmp will be invalid as soon as - * we exit this routine. If there is an outer MainLoop instance, it will - * re-enable ^C catching as soon as it gets back to the top of its loop - * and resets main_loop_jmp to point to itself. - */ - #ifndef WIN32 - pqsignal(SIGINT, SIG_DFL); - #endif - destroyPQExpBuffer(query_buf); destroyPQExpBuffer(previous_buf); --- 298,303 ---- Index: src/bin/psql/mainloop.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/mainloop.h,v retrieving revision 1.17 diff -c -r1.17 mainloop.h *** src/bin/psql/mainloop.h 1 Jan 2005 05:43:08 -0000 1.17 --- src/bin/psql/mainloop.h 22 Oct 2005 19:11:41 -0000 *************** *** 10,20 **** #include "postgres_fe.h" #include - #ifndef WIN32 - #include - - extern sigjmp_buf main_loop_jmp; - #endif int MainLoop(FILE *source); --- 10,15 ---- Index: src/bin/psql/print.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.78 diff -c -r1.78 print.c *** src/bin/psql/print.c 15 Oct 2005 02:49:40 -0000 1.78 --- src/bin/psql/print.c 22 Oct 2005 19:11:41 -0000 *************** *** 184,190 **** /* print cells */ i = 0; ! for (ptr = cells; *ptr; ptr++) { if (need_recordsep) { --- 184,190 ---- /* print cells */ i = 0; ! for (ptr = cells; *ptr && !cancel_pressed; ptr++) { if (need_recordsep) { *************** *** 211,217 **** /* print footers */ if (!opt_tuples_only && footers) ! for (ptr = footers; *ptr; ptr++) { if (need_recordsep) { --- 211,217 ---- /* print footers */ if (!opt_tuples_only && footers) ! for (ptr = footers; *ptr && !cancel_pressed; ptr++) { if (need_recordsep) { *************** *** 254,260 **** col_count++; /* print records */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { if (i != 0 || (!opt_tuples_only && title)) { --- 254,260 ---- col_count++; /* print records */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { if (i != 0 || (!opt_tuples_only && title)) { *************** *** 277,283 **** } /* print footers */ ! if (!opt_tuples_only && footers && *footers) { fputs(opt_recordsep, fout); for (ptr = footers; *ptr; ptr++) --- 277,283 ---- } /* print footers */ ! if (!opt_tuples_only && footers && *footers && !cancel_pressed) { fputs(opt_recordsep, fout); for (ptr = footers; *ptr; ptr++) *************** *** 393,399 **** cell_w = NULL; /* calc column widths */ ! for (i = 0; i < col_count; i++) { tmp = pg_wcswidth(headers[i], strlen(headers[i]), encoding); if (tmp > widths[i]) --- 393,399 ---- cell_w = NULL; /* calc column widths */ ! for (i = 0; i < col_count && !cancel_pressed; i++) { tmp = pg_wcswidth(headers[i], strlen(headers[i]), encoding); if (tmp > widths[i]) *************** *** 401,407 **** head_w[i] = tmp; } ! for (i = 0, ptr = cells; *ptr; ptr++, i++) { int add_numeric_locale_len; --- 401,407 ---- head_w[i] = tmp; } ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; ptr++, i++) { int add_numeric_locale_len; *************** *** 447,453 **** else if (opt_border == 1) fputc(' ', fout); ! for (i = 0; i < col_count; i++) { unsigned int nbspace; --- 447,453 ---- else if (opt_border == 1) fputc(' ', fout); ! for (i = 0; i < col_count && !cancel_pressed; i++) { unsigned int nbspace; *************** *** 476,482 **** } /* print cells */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { /* beginning of line */ if (i % col_count == 0) --- 476,482 ---- } /* print cells */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { /* beginning of line */ if (i % col_count == 0) *************** *** 530,536 **** _print_horizontal_line(col_count, widths, opt_border, fout); /* print footers */ ! if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) fprintf(fout, "%s\n", *ptr); --- 530,536 ---- _print_horizontal_line(col_count, widths, opt_border, fout); /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) for (ptr = footers; *ptr; ptr++) fprintf(fout, "%s\n", *ptr); *************** *** 599,605 **** } /* Count cells, find their lengths */ ! for (ptr = cells; *ptr; ptr++) cell_count++; if (cell_count > 0) --- 599,605 ---- } /* Count cells, find their lengths */ ! for (ptr = cells; *ptr && !cancel_pressed; ptr++) cell_count++; if (cell_count > 0) *************** *** 615,621 **** cell_w = NULL; /* find longest data cell */ ! for (i = 0, ptr = cells; *ptr; ptr++, i++) { int add_numeric_locale_len; --- 615,621 ---- cell_w = NULL; /* find longest data cell */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; ptr++, i++) { int add_numeric_locale_len; *************** *** 651,657 **** strcat(divider, "-+"); /* print records */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { if (i % col_count == 0) { --- 651,657 ---- strcat(divider, "-+"); /* print records */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { if (i % col_count == 0) { *************** *** 722,728 **** /* print footers */ ! if (!opt_tuples_only && footers && *footers) { if (opt_border < 2) fputc('\n', fout); --- 722,728 ---- /* print footers */ ! if (!opt_tuples_only && footers && *footers && !cancel_pressed) { if (opt_border < 2) fputc('\n', fout); *************** *** 832,838 **** fputs(" \n", fout); /* print cells */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { if (i % col_count == 0) fputs(" \n", fout); --- 832,838 ---- fputs(" \n", fout); /* print cells */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { if (i % col_count == 0) fputs(" \n", fout); *************** *** 861,867 **** /* print footers */ ! if (!opt_tuples_only && footers && *footers) { fputs("

", fout); for (ptr = footers; *ptr; ptr++) --- 861,867 ---- /* print footers */ ! if (!opt_tuples_only && footers && *footers && !cancel_pressed) { fputs("

", fout); for (ptr = footers; *ptr; ptr++) *************** *** 906,912 **** col_count++; /* print records */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { if (i % col_count == 0) { --- 906,912 ---- col_count++; /* print records */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { if (i % col_count == 0) { *************** *** 940,946 **** fputs("\n", fout); /* print footers */ ! if (!opt_tuples_only && footers && *footers) { fputs("

", fout); for (ptr = footers; *ptr; ptr++) --- 940,946 ---- fputs("\n", fout); /* print footers */ ! if (!opt_tuples_only && footers && *footers && !cancel_pressed) { fputs("

", fout); for (ptr = footers; *ptr; ptr++) *************** *** 1043,1049 **** fputs("\\hline\n", fout); /* print headers and count columns */ ! for (i = 0, ptr = headers; i < col_count; i++, ptr++) { if (!opt_tuples_only) { --- 1043,1049 ---- fputs("\\hline\n", fout); /* print headers and count columns */ ! for (i = 0, ptr = headers; i < col_count && !cancel_pressed; i++, ptr++) { if (!opt_tuples_only) { *************** *** 1062,1068 **** } /* print cells */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { if (opt_numeric_locale) { --- 1062,1068 ---- } /* print cells */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { if (opt_numeric_locale) { *************** *** 1088,1094 **** /* print footers */ ! if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { latex_escaped_print(*ptr, fout); --- 1088,1094 ---- /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) for (ptr = footers; *ptr; ptr++) { latex_escaped_print(*ptr, fout); *************** *** 1139,1145 **** /* print records */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { /* new record */ if (i % col_count == 0) --- 1139,1145 ---- /* print records */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { /* new record */ if (i % col_count == 0) *************** *** 1172,1178 **** /* print footers */ ! if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { if (opt_numeric_locale) --- 1172,1178 ---- /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) for (ptr = footers; *ptr; ptr++) { if (opt_numeric_locale) *************** *** 1255,1261 **** fputs(".\n", fout); /* print headers and count columns */ ! for (i = 0, ptr = headers; i < col_count; i++, ptr++) { if (!opt_tuples_only) { --- 1255,1261 ---- fputs(".\n", fout); /* print headers and count columns */ ! for (i = 0, ptr = headers; i < col_count && !cancel_pressed; i++, ptr++) { if (!opt_tuples_only) { *************** *** 1271,1277 **** fputs("\n_\n", fout); /* print cells */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { if (opt_numeric_locale) { --- 1271,1277 ---- fputs("\n_\n", fout); /* print cells */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { if (opt_numeric_locale) { *************** *** 1294,1300 **** /* print footers */ ! if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { troff_ms_escaped_print(*ptr, fout); --- 1294,1300 ---- /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) for (ptr = footers; *ptr; ptr++) { troff_ms_escaped_print(*ptr, fout); *************** *** 1345,1351 **** col_count++; /* print records */ ! for (i = 0, ptr = cells; *ptr; i++, ptr++) { /* new record */ if (i % col_count == 0) --- 1345,1351 ---- col_count++; /* print records */ ! for (i = 0, ptr = cells; *ptr && !cancel_pressed; i++, ptr++) { /* new record */ if (i % col_count == 0) *************** *** 1401,1407 **** /* print footers */ ! if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { troff_ms_escaped_print(*ptr, fout); --- 1401,1407 ---- /* print footers */ ! if (footers && !opt_tuples_only && !cancel_pressed) for (ptr = footers; *ptr; ptr++) { troff_ms_escaped_print(*ptr, fout); *************** *** 1479,1484 **** --- 1479,1487 ---- if (opt->format == PRINT_NOTHING) return; + + if( cancel_pressed ) + return; if (!footers) footers = default_footer; *************** *** 1590,1595 **** --- 1593,1607 ---- /* Only close if we used the pager */ if (fout == stdout && output != stdout) { + /* Some pagers like less use Ctrl-C as part of their command + * set. Even so, we abort our processing and warn the user + * what we did. If the pager quit as a result of the + * SIGINT, this message won't go anywhere but they'll still + * see the other message from the main loop. */ + + if( cancel_pressed ) + fprintf( output, "Interrupted\n" ); + pclose(output); #ifndef WIN32 pqsignal(SIGPIPE, SIG_DFL); *************** *** 1610,1615 **** --- 1622,1630 ---- char *align; int i; + if( cancel_pressed ) + return; + /* extract headers */ nfields = PQnfields(result); *************** *** 1632,1638 **** exit(EXIT_FAILURE); } ! for (i = 0; i < ncells; i++) { if (PQgetisnull(result, i / nfields, i % nfields)) cells[i] = opt->nullPrint ? opt->nullPrint : ""; --- 1647,1653 ---- exit(EXIT_FAILURE); } ! for (i = 0; i < ncells && !cancel_pressed; i++) { if (PQgetisnull(result, i / nfields, i % nfields)) cells[i] = opt->nullPrint ? opt->nullPrint : ""; *************** *** 1670,1676 **** exit(EXIT_FAILURE); } ! for (i = 0; i < nfields; i++) { Oid ftype = PQftype(result, i); --- 1685,1691 ---- exit(EXIT_FAILURE); } ! for (i = 0; i < nfields && !cancel_pressed; i++) { Oid ftype = PQftype(result, i); *************** *** 1689,1697 **** } /* call table printer */ ! printTable(opt->title, headers, cells, ! (const char *const *) footers, ! align, &opt->topt, fout, flog); free(headers); free(cells); --- 1704,1713 ---- } /* call table printer */ ! if( !cancel_pressed ) ! printTable(opt->title, headers, cells, ! (const char *const *) footers, ! align, &opt->topt, fout, flog); free(headers); free(cells); Index: src/bin/scripts/common.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/scripts/common.c,v retrieving revision 1.19 diff -c -r1.19 common.c *** src/bin/scripts/common.c 15 Oct 2005 02:49:41 -0000 1.19 --- src/bin/scripts/common.c 22 Oct 2005 19:11:41 -0000 *************** *** 19,24 **** --- 19,27 ---- #include "common.h" + /* This variable is not used in ../scripts, but is required because print.c needs it */ + volatile bool cancel_pressed; + #ifndef HAVE_INT_OPTRESET int optreset; #endif Index: src/bin/scripts/common.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/scripts/common.h,v retrieving revision 1.12 diff -c -r1.12 common.h *** src/bin/scripts/common.h 15 Oct 2005 02:49:41 -0000 1.12 --- src/bin/scripts/common.h 22 Oct 2005 19:11:41 -0000 *************** *** 17,22 **** --- 17,25 ---- extern int optreset; #endif + /* This variable is not used in ../scripts, but is required because print.c needs it */ + extern volatile bool cancel_pressed; + typedef void (*help_handler) (const char *progname); extern const char *get_user_name(const char *progname);