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 */