diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index e4a7dd9..06c8fce 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -51,6 +51,8 @@ /* GUC variables */ int DeadlockTimeout = 1000; int StatementTimeout = 0; +int SessionTimerTarget = 0; + bool log_lock_waits = false; /* Pointer to this process's PGPROC struct, if any */ @@ -1550,6 +1552,10 @@ CheckStatementTimeout(void) /* Time to die */ statement_timeout_active = false; cancel_from_timeout = true; + + /* reset session timer. Never fire twice. */ + set_session_timer_ms(0); + #ifdef HAVE_SETSID /* try to signal whole process group */ kill(-MyProcPid, SIGINT); diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index cba90a9..769ac2c 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -2445,28 +2445,43 @@ exec_describe_portal_message(const char *portal_name) pq_putemptymessage('n'); /* NoData */ } - /* * Convenience routines for starting/committing a single command. */ static void start_xact_command(void) { + int64 timeout = 0; + if (!xact_started) { - ereport(DEBUG3, - (errmsg_internal("StartTransactionCommand"))); + ereport(DEBUG3, (errmsg_internal("StartTransactionCommand"))); StartTransactionCommand(); - /* Set statement timeout running, if any */ - /* NB: this mustn't be enabled until we are within an xact */ - if (StatementTimeout > 0) - enable_sig_alarm(StatementTimeout, true); - else - cancel_from_timeout = false; + if (timeout == 0 || + (StatementTimeout > 0 && timeout > StatementTimeout)) { + timeout = StatementTimeout; + } + + /* Set statement timeout running, if any */ + /* NB: this mustn't be enabled until we are within an xact */ + if (StatementTimeout > 0) + enable_sig_alarm(StatementTimeout, true); + else + cancel_from_timeout = false; xact_started = true; - } + } + + timeout = get_session_timer_ms(); + if (timeout > 0) { + if (StatementTimeout == 0 || timeout < StatementTimeout) { + ereport(DEBUG3, (errmsg_internal("Enable an once session timer"))); + enable_sig_alarm(timeout, true); + } else { + cancel_from_timeout = false; + } + } } static void diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index c6e1d13..d998df6 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -4824,3 +4824,50 @@ generate_series_timestamptz(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } } + +/* Session timer target, in ms */ +static int64 sessionTimerTarget; + +Datum set_session_timer(PG_FUNCTION_ARGS) +{ + int64 ms = PG_GETARG_INT64(0); + set_session_timer_ms(ms); + PG_RETURN_DATUM(0); +} + +#ifndef HAVE_INT64_TIMESTAMP +#error Assumes int64 impl of timestamp +#endif + +void set_session_timer_ms(int64 ms) +{ + if (ms == 0) { + sessionTimerTarget = 0; + } else { + /* GetCurrentTimestamp is in us */ + int64 time_now = GetCurrentTimestamp() / 1000; + sessionTimerTarget = time_now + ms; + } +} + +int64 get_session_timer_ms() +{ + int64 ret = 0; + if (sessionTimerTarget != 0) { + int64 time_now = GetCurrentTimestamp() / 1000; + ret = sessionTimerTarget - time_now; + + if (ret <= 0) { + /* Timer already passed. This maybe possible if some statement + * set the timer, finished so the timer is disabled, next statement + * set the timer again, but too late. We want to go through the + * same routine and fire the timer. Return 1 ms. + */ + ret = 1; + } + + sessionTimerTarget = 0; + } + + return ret; +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 910474c..904a821 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201010101 +#define CATALOG_VERSION_NO 201010102 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 61c6b27..308f014 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4849,6 +4849,8 @@ DATA(insert OID = 3113 ( last_value PGNSP PGUID 12 1 0 0 f t f t f i 1 0 2283 " DESCR("fetch the last row value"); DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ )); DESCR("fetch the Nth row value"); +DATA(insert OID = 3115 ( set_session_timer PGNSP PGUID 12 1 0 0 f f f t f i 1 0 20 "20" _null_ _null_ _null_ _null_ set_session_timer _null_ _null_ _null_ )); +DESCR("Set a timer for the session"); /* diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index db7b729..c60d109 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -310,6 +310,11 @@ extern Datum pg_conf_load_time(PG_FUNCTION_ARGS); extern Datum generate_series_timestamp(PG_FUNCTION_ARGS); extern Datum generate_series_timestamptz(PG_FUNCTION_ARGS); +/* session timer, for jdbc setQueryTimeout and like */ +extern Datum set_session_timer(PG_FUNCTION_ARGS); +extern void set_session_timer_ms(int64 ms); +extern int64 get_session_timer_ms(void); + /* Internal routines (not fmgr-callable) */ extern TimestampTz GetCurrentTimestamp(void);