diff --git a/doc/src/sgml/ref/wait.sgml b/doc/src/sgml/ref/wait.sgml index 2917309c..c7d594a9 100644 --- a/doc/src/sgml/ref/wait.sgml +++ b/doc/src/sgml/ref/wait.sgml @@ -161,6 +161,9 @@ WAIT FOR LSN 'lsn' less therefore rounds down to zero, which means waiting indefinitely. + + Negative timeout values are rejected with an error. + diff --git a/src/backend/commands/wait.c b/src/backend/commands/wait.c index a609f637..bd2aad90 100644 --- a/src/backend/commands/wait.c +++ b/src/backend/commands/wait.c @@ -13,6 +13,8 @@ */ #include "postgres.h" +#include + #include "access/xact.h" #include "access/xlog.h" #include "access/xlogrecovery.h" @@ -107,6 +109,28 @@ ExecWaitStmt(ParseState *pstate, WaitStmt *stmt, bool isTopLevel, hintmsg ? errhint("%s", _(hintmsg)) : 0, parser_errposition(pstate, defel->location)); + /* + * parse_int() rounds fractional values to integer before we + * get a chance to check them, so a negative timeout smaller + * than half a millisecond (e.g. '-0.4ms') would otherwise + * round to zero and silently become an infinite wait. Reject + * any negative input up front: parse_int() accepts the sign + * syntaxes of strtol()/strtod(), where a negative value + * necessarily starts with a '-'. + */ + { + const char *cp = timeout_str; + + while (isspace((unsigned char) *cp)) + cp++; + + if (*cp == '-') + ereport(ERROR, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("timeout cannot be negative"), + parser_errposition(pstate, defel->location)); + } + if (timeout < 0) ereport(ERROR, errcode(ERRCODE_INVALID_PARAMETER_VALUE), diff --git a/src/test/recovery/t/049_wait_for_lsn.pl b/src/test/recovery/t/049_wait_for_lsn.pl index 0fe5b9c6..ea9eefe2 100644 --- a/src/test/recovery/t/049_wait_for_lsn.pl +++ b/src/test/recovery/t/049_wait_for_lsn.pl @@ -367,6 +367,25 @@ $node_standby->psql( stderr => \$stderr); ok($stderr =~ /timeout cannot be negative/, "get error for negative timeout"); +# Test negative sub-millisecond timeout: rint() would round these to zero +# before the < 0 check, silently turning them into an infinite wait +for my $neg_timeout (qw(-0.4ms -0.49ms -0.4999999ms -0.000001s -0)) +{ + $node_standby->psql( + 'postgres', + "WAIT FOR LSN '${test_lsn}' WITH (timeout '${neg_timeout}');", + stderr => \$stderr); + ok($stderr =~ /timeout cannot be negative/, + "get error for negative sub-millisecond timeout '${neg_timeout}'"); + + $node_standby->psql( + 'postgres', + "WAIT FOR LSN '${test_lsn}' WITH (timeout ' ${neg_timeout} ');", + stderr => \$stderr); + ok($stderr =~ /timeout cannot be negative/, + "get error for negative timeout '${neg_timeout}' with leading space"); +} + # Test out of range timeout $node_standby->psql( 'postgres',