aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2002-07-13 01:02:14 +0000
committerBruce Momjian <bruce@momjian.us>2002-07-13 01:02:14 +0000
commit4db8718e84a40125e8a7c2ffcfccf50932cdfd85 (patch)
tree8bc5fe0215602ad80001676193f47918ad1df29d
parentccb3f90f0695d87e29ee066dd64183fa29edfc3f (diff)
downloadpostgresql-4db8718e84a40125e8a7c2ffcfccf50932cdfd85.tar.gz
postgresql-4db8718e84a40125e8a7c2ffcfccf50932cdfd85.zip
Add SET statement_timeout capability. Timeout is in ms. A value of
zero turns off the timer.
-rw-r--r--doc/src/sgml/runtime.sgml12
-rw-r--r--src/backend/postmaster/postmaster.c6
-rw-r--r--src/backend/storage/lmgr/proc.c180
-rw-r--r--src/backend/tcop/postgres.c24
-rw-r--r--src/backend/utils/misc/guc.c8
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample1
-rw-r--r--src/bin/psql/tab-complete.c3
-rw-r--r--src/include/storage/proc.h9
8 files changed, 200 insertions, 43 deletions
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 07e0575b368..15f2e6e56af 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">
@@ -1585,6 +1585,16 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir'
</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>
<para>
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 349fe155c09..f84bf27e92b 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 012278ab402..53dffec6423 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 c6b8fcfafd6..154e478687b 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 53503f4b413..5162509b261 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;
@@ -576,6 +577,11 @@ static struct config_int
},
{
+ { "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 00dced8d394..bf141249ae0 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 521742ea1dd..24767172451 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 71b86cc8b36..b31b0bfe002 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 */