diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 07e0575b36831dab66829ab86ca6ed3b75828427..15f2e6e56af4105a2ca2f51fe01c2fd4e040e99c 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.120 2002/07/05 01:17:20 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.121 2002/07/13 01:02:14 momjian Exp $ --> <Chapter Id="runtime"> @@ -1584,6 +1584,16 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir' </listitem> </varlistentry> + <varlistentry> + <term><varname>STATEMENT_TIMEOUT</varname> (<type>integer</type>)</term> + <listitem> + <para> + Aborts any statement that takes over the specified number of + microseconds. A value of zero turns off the timer. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>SHARED_BUFFERS</varname> (<type>integer</type>)</term> <listitem> diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 349fe155c09a78e43dfb2e695d960417f236fb1f..f84bf27e92ba2778bfd206c6f92a09432371d8ca 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.280 2002/06/20 20:29:33 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.281 2002/07/13 01:02:14 momjian Exp $ * * NOTES * @@ -2105,7 +2105,7 @@ DoBackend(Port *port) * after a time delay, so that a broken client can't hog a connection * indefinitely. PreAuthDelay doesn't count against the time limit. */ - if (!enable_sigalrm_interrupt(AuthenticationTimeout * 1000)) + if (!enable_sig_alarm(AuthenticationTimeout * 1000, false)) elog(FATAL, "DoBackend: Unable to set timer for auth timeout"); /* @@ -2134,7 +2134,7 @@ DoBackend(Port *port) * Done with authentication. Disable timeout, and prevent * SIGTERM/SIGQUIT again until backend startup is complete. */ - if (!disable_sigalrm_interrupt()) + if (!disable_sig_alarm(false)) elog(FATAL, "DoBackend: Unable to disable timer for auth timeout"); PG_SETMASK(&BlockSig); diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 012278ab4027ac1645a00c49542baf914895ca26..53dffec64237ebf33217e36818ddcabce09afc6b 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.121 2002/06/20 20:29:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.122 2002/07/13 01:02:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -52,8 +52,10 @@ #include "storage/sinval.h" #include "storage/spin.h" - int DeadlockTimeout = 1000; +int StatementTimeout = 0; +int RemainingStatementTimeout = 0; +bool alarm_is_statement_timeout = false; PGPROC *MyProc = NULL; @@ -319,7 +321,7 @@ LockWaitCancel(void) waitingForLock = false; /* Turn off the deadlock timer, if it's still running (see ProcSleep) */ - disable_sigalrm_interrupt(); + disable_sig_alarm(false); /* Unlink myself from the wait queue, if on it (might not be anymore!) */ LWLockAcquire(LockMgrLock, LW_EXCLUSIVE); @@ -600,7 +602,7 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable, /* * If we detected deadlock, give up without waiting. This must agree - * with HandleDeadLock's recovery code, except that we shouldn't + * with CheckDeadLock's recovery code, except that we shouldn't * release the semaphore since we haven't tried to lock it yet. */ if (early_deadlock) @@ -632,13 +634,13 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable, * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. */ - if (!enable_sigalrm_interrupt(DeadlockTimeout)) + if (!enable_sig_alarm(DeadlockTimeout, false)) elog(FATAL, "ProcSleep: Unable to set timer for process wakeup"); /* * If someone wakes us between LWLockRelease and PGSemaphoreLock, * PGSemaphoreLock will not block. The wakeup is "saved" by the - * semaphore implementation. Note also that if HandleDeadLock is + * semaphore implementation. Note also that if CheckDeadLock is * invoked but does not detect a deadlock, PGSemaphoreLock() will * continue to wait. There used to be a loop here, but it was useless * code... @@ -654,7 +656,7 @@ ProcSleep(LOCKMETHODTABLE *lockMethodTable, /* * Disable the timer, if it's still running */ - if (!disable_sigalrm_interrupt()) + if (!disable_sig_alarm(false)) elog(FATAL, "ProcSleep: Unable to disable timer for process wakeup"); /* @@ -785,7 +787,7 @@ ProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock) * -------------------- */ void -HandleDeadLock(SIGNAL_ARGS) +CheckDeadLock(void) { int save_errno = errno; @@ -921,52 +923,180 @@ ProcSendSignal(BackendId procId) * Delay is given in milliseconds. Caller should be sure a SIGALRM * signal handler is installed before this is called. * + * This code properly handles multiple alarms when the statement_timeout + * alarm is specified first. + * * Returns TRUE if okay, FALSE on failure. */ bool -enable_sigalrm_interrupt(int delayms) +enable_sig_alarm(int delayms, bool is_statement_timeout) { #ifndef __BEOS__ - struct itimerval timeval, - dummy; + struct itimerval timeval, remaining; +#else + bigtime_t time_interval, remaining; +#endif + /* Don't set timer if the statement timeout scheduled before next alarm. */ + if (alarm_is_statement_timeout && + !is_statement_timeout && + RemainingStatementTimeout <= delayms) + return true; + +#ifndef __BEOS__ MemSet(&timeval, 0, sizeof(struct itimerval)); timeval.it_value.tv_sec = delayms / 1000; timeval.it_value.tv_usec = (delayms % 1000) * 1000; - if (setitimer(ITIMER_REAL, &timeval, &dummy)) + if (setitimer(ITIMER_REAL, &timeval, &remaining)) return false; #else /* BeOS doesn't have setitimer, but has set_alarm */ - bigtime_t time_interval; - time_interval = delayms * 1000; /* usecs */ - if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0) + if ((remaining = set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM)) < 0) return false; #endif + if (is_statement_timeout) + RemainingStatementTimeout = StatementTimeout; + else + { + /* Switching to non-statement-timeout alarm, get remaining time */ + if (alarm_is_statement_timeout) + { +#ifndef __BEOS__ + /* We lose precision here because we convert to milliseconds */ + RemainingStatementTimeout = remaining.it_value.tv_sec * 1000 + + remaining.it_value.tv_usec / 1000; +#else + RemainingStatementTimeout = remaining / 1000; +#endif + /* Rounding could cause a zero */ + if (RemainingStatementTimeout == 0) + RemainingStatementTimeout = 1; + } + + if (RemainingStatementTimeout) + { + /* Remaining timeout alarm < delayms? */ + if (RemainingStatementTimeout <= delayms) + { + /* reinstall statement timeout alarm */ + alarm_is_statement_timeout = true; +#ifndef __BEOS__ + remaining.it_value.tv_sec = RemainingStatementTimeout / 1000; + remaining.it_value.tv_usec = (RemainingStatementTimeout % 1000) * 1000; + if (setitimer(ITIMER_REAL, &remaining, &timeval)) + return false; + else + return true; +#else + remaining = RemainingStatementTimeout * 1000; + if ((timeval = set_alarm(remaining, B_ONE_SHOT_RELATIVE_ALARM)) < 0) + return false; + else + return true; +#endif + } + else + RemainingStatementTimeout -= delayms; + } + } + + if (is_statement_timeout) + alarm_is_statement_timeout = true; + else + alarm_is_statement_timeout = false; + return true; } /* - * Disable the SIGALRM interrupt, if it has not yet fired + * Cancel the SIGALRM timer. + * + * This is also called if the timer has fired to reschedule + * the statement_timeout timer. * * Returns TRUE if okay, FALSE on failure. */ bool -disable_sigalrm_interrupt(void) +disable_sig_alarm(bool is_statement_timeout) { #ifndef __BEOS__ - struct itimerval timeval, - dummy; - + struct itimerval timeval, remaining; MemSet(&timeval, 0, sizeof(struct itimerval)); - if (setitimer(ITIMER_REAL, &timeval, &dummy)) - return false; #else - /* BeOS doesn't have setitimer, but has set_alarm */ - if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0) - return false; + bigtime_t time_interval = 0; #endif + if (!is_statement_timeout && RemainingStatementTimeout) + { +#ifndef __BEOS__ + /* turn off timer and get remaining time, if any */ + if (setitimer(ITIMER_REAL, &timeval, &remaining)) + return false; + /* Add remaining time back because the timer didn't complete */ + RemainingStatementTimeout += remaining.it_value.tv_sec * 1000 + + remaining.it_value.tv_usec / 1000; + /* Prepare to set timer */ + timeval.it_value.tv_sec = RemainingStatementTimeout / 1000; + timeval.it_value.tv_usec = (RemainingStatementTimeout % 1000) * 1000; +#else + /* BeOS doesn't have setitimer, but has set_alarm */ + if ((time_interval = set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM)) < 0) + return false; + RemainingStatementTimeout += time_interval / 1000; + time_interval = RemainingStatementTimeout * 1000; +#endif + /* Restore remaining statement timeout value */ + alarm_is_statement_timeout = true; + } + /* + * Optimization: is_statement_timeout && RemainingStatementTimeout == 0 + * does nothing. This is for cases where no timeout was set. + */ + if (!is_statement_timeout || RemainingStatementTimeout) + { +#ifndef __BEOS__ + if (setitimer(ITIMER_REAL, &timeval, &remaining)) + return false; +#else + if (time_interval) + { + if (set_alarm(time_interval, B_ONE_SHOT_RELATIVE_ALARM) < 0) + return false; + } + else + { + if (set_alarm(B_INFINITE_TIMEOUT, B_PERIODIC_ALARM) < 0) + return false; + } +#endif + } + + if (is_statement_timeout) + RemainingStatementTimeout = 0; + return true; } + + +/* + * Call alarm handler, either StatementCancel or Deadlock checker. + */ +void +handle_sig_alarm(SIGNAL_ARGS) +{ + if (alarm_is_statement_timeout) + { + RemainingStatementTimeout = 0; + alarm_is_statement_timeout = false; + kill(MyProcPid, SIGINT); + } + else + { + CheckDeadLock(); + /* Reactivate any statement_timeout alarm. */ + disable_sig_alarm(false); + } +} + diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index c6b8fcfafd6c0394a39d21b1e141a92c25691c50..154e478687b673a28be8d6085f67e45ab6610879 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.269 2002/07/11 07:39:26 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.270 2002/07/13 01:02:14 momjian Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -78,6 +78,8 @@ char *debug_query_string; /* used by pgmonitor */ /* Note: whereToSendOutput is initialized for the bootstrap/standalone case */ CommandDest whereToSendOutput = Debug; +extern int StatementTimeout; + static bool dontExecute = false; /* note: these declarations had better match tcopprot.h */ @@ -717,6 +719,9 @@ pg_exec_query_string(StringInfo query_string, /* string to execute */ xact_started = true; } + if (StatementTimeout) + enable_sig_alarm(StatementTimeout, true); + /* * If we got a cancel signal in analysis or prior command, * quit @@ -791,6 +796,8 @@ pg_exec_query_string(StringInfo query_string, /* string to execute */ ShowUsage("EXECUTOR STATISTICS"); } + disable_sig_alarm(true); + /* * In a query block, we want to increment the command counter * between queries so that the effects of early queries are @@ -821,9 +828,7 @@ pg_exec_query_string(StringInfo query_string, /* string to execute */ finish_xact_command(); xact_started = false; } - - } /* end loop over queries generated from a - * parsetree */ + } /* end loop over queries generated from a parsetree */ /* * If this is the last parsetree of the query string, close down @@ -996,7 +1001,7 @@ authdie(SIGNAL_ARGS) * at soonest convenient time */ static void -QueryCancelHandler(SIGNAL_ARGS) +StatementCancelHandler(SIGNAL_ARGS) { int save_errno = errno; @@ -1551,10 +1556,10 @@ PostgresMain(int argc, char *argv[], const char *username) */ pqsignal(SIGHUP, SigHupHandler); /* set flag to read config file */ - pqsignal(SIGINT, QueryCancelHandler); /* cancel current query */ + pqsignal(SIGINT, StatementCancelHandler); /* cancel current query */ pqsignal(SIGTERM, die); /* cancel current query and exit */ pqsignal(SIGQUIT, quickdie); /* hard crash time */ - pqsignal(SIGALRM, HandleDeadLock); /* check for deadlock after + pqsignal(SIGALRM, handle_sig_alarm); /* check for deadlock after * timeout */ /* @@ -1688,7 +1693,7 @@ PostgresMain(int argc, char *argv[], const char *username) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.269 $ $Date: 2002/07/11 07:39:26 $\n"); + puts("$Revision: 1.270 $ $Date: 2002/07/13 01:02:14 $\n"); } /* @@ -1820,6 +1825,9 @@ PostgresMain(int argc, char *argv[], const char *username) QueryCancelPending = false; /* forget any earlier CANCEL * signal */ + /* Stop any statement timer */ + disable_sig_alarm(true); + EnableNotifyInterrupt(); /* Allow "die" interrupt to be processed while waiting */ diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 53503f4b413c708248010641376454ec0dcbfb23..5162509b26190e5173b0e3fe587f416256b42168 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -5,7 +5,7 @@ * command, configuration file, and command line options. * See src/backend/utils/misc/README for more information. * - * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.70 2002/06/16 00:09:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.71 2002/07/13 01:02:14 momjian Exp $ * * Copyright 2000 by PostgreSQL Global Development Group * Written by Peter Eisentraut <peter_e@gmx.net>. @@ -51,6 +51,7 @@ extern bool Log_connections; extern int PreAuthDelay; extern int AuthenticationTimeout; +extern int StatementTimeout; extern int CheckPointTimeout; extern int CommitDelay; extern int CommitSiblings; @@ -575,6 +576,11 @@ static struct config_int DEFAULT_MAX_EXPR_DEPTH, 10, INT_MAX, NULL, NULL }, + { + { "statement_timeout", PGC_USERSET }, &StatementTimeout, + 0, 0, INT_MAX, NULL, NULL + }, + { { "max_fsm_relations", PGC_POSTMASTER }, &MaxFSMRelations, 100, 10, INT_MAX, NULL, NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 00dced8d394ae3fa91bcb3eb52057df22197b09b..bf141249ae043ffb2fbc66862ed1d25188c02255 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -200,3 +200,4 @@ #password_encryption = true #sql_inheritance = true #transform_null_equals = false +#statement_timeout = 0 # 0 is disabled diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 521742ea1dd22fb4952802c99a8948963da0adaa..24767172451baa7ed90f64c732e6878e95c6ca15 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.50 2002/06/16 00:09:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.51 2002/07/13 01:02:14 momjian Exp $ */ /*---------------------------------------------------------------------- @@ -267,6 +267,7 @@ psql_completion(char *text, int start, int end) "default_transaction_isolation", "search_path", + "statement_timeout", NULL }; diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 71b86cc8b3624992a6c31f879ce997f8f8e9cd3e..b31b0bfe002cf3c236cc837cc049e890009e4802 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: proc.h,v 1.57 2002/06/20 20:29:52 momjian Exp $ + * $Id: proc.h,v 1.58 2002/07/13 01:02:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -105,13 +105,14 @@ extern int ProcSleep(LOCKMETHODTABLE *lockMethodTable, LOCKMODE lockmode, extern PGPROC *ProcWakeup(PGPROC *proc, int errType); extern void ProcLockWakeup(LOCKMETHODTABLE *lockMethodTable, LOCK *lock); extern bool LockWaitCancel(void); -extern void HandleDeadLock(SIGNAL_ARGS); +extern void CheckDeadLock(void); extern void ProcWaitForSignal(void); extern void ProcCancelWaitForSignal(void); extern void ProcSendSignal(BackendId procId); -extern bool enable_sigalrm_interrupt(int delayms); -extern bool disable_sigalrm_interrupt(void); +extern bool enable_sig_alarm(int delayms, bool is_statement_timeout); +extern bool disable_sig_alarm(bool is_statement_timeout); +extern void handle_sig_alarm(SIGNAL_ARGS); #endif /* PROC_H */