mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 01:53:41 +08:00
Fix chunk exclusion for prepared statements and dst changes
The constify code constifying TIMESTAMPTZ expressions when doing chunk exclusion did not account for daylight saving time switches leading to different calculation outcomes when timezone changes. This patch adds a 4 hour safety buffer to any such calculations.
This commit is contained in:
parent
217f514657
commit
2529ae3f68
@ -20,10 +20,11 @@ argument or resolve the type ambiguity by casting to the intended type.
|
||||
**Bugfixes**
|
||||
* #4619 Improve handling enum columns in compressed hypertables
|
||||
* #4673 Fix now() constification for VIEWs
|
||||
* #4676 Fix a deadlock when decompressing chunks and performing SELECTs
|
||||
* #4681 Fix compression_chunk_size primary key
|
||||
* #4685 Improve chunk exclusion for space dimensions
|
||||
* #4696 Report warning when enabling compression on hypertable
|
||||
* #4676 Fix a deadlock when decompressing chunks and performing SELECTs
|
||||
* #4720 Fix chunk exclusion for prepared statements and dst changes
|
||||
* #4739 Fix continuous aggregate migrate check constraint
|
||||
|
||||
**Thanks**
|
||||
|
@ -547,6 +547,8 @@ get_reindex_options(ReindexStmt *stmt)
|
||||
* timestamp_gt.
|
||||
*/
|
||||
#if PG14_LT
|
||||
#define F_TIMESTAMPTZ_LE F_TIMESTAMP_LE
|
||||
#define F_TIMESTAMPTZ_LT F_TIMESTAMP_LT
|
||||
#define F_TIMESTAMPTZ_GE F_TIMESTAMP_GE
|
||||
#define F_TIMESTAMPTZ_GT F_TIMESTAMP_GT
|
||||
#endif
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <partitioning/partbounds.h>
|
||||
#include <utils/date.h>
|
||||
#include <utils/errcodes.h>
|
||||
#include <utils/fmgroids.h>
|
||||
#include <utils/fmgrprotos.h>
|
||||
#include <utils/syscache.h>
|
||||
|
||||
@ -268,6 +269,47 @@ constify_timestamptz_op_interval(PlannerInfo *root, OpExpr *constraint)
|
||||
|
||||
constified = DirectFunctionCall2(opfunc, c_ts->constvalue, c_int->constvalue);
|
||||
|
||||
/*
|
||||
* Since constifying intervals with day component does depend on the timezone
|
||||
* this can lead to different results around daylight saving time switches.
|
||||
* So we add a safety buffer when the interval has day components to counteract.
|
||||
*/
|
||||
if (interval->day != 0)
|
||||
{
|
||||
bool add;
|
||||
TimestampTz constified_tstz = DatumGetTimestampTz(constified);
|
||||
|
||||
switch (constraint->opfuncid)
|
||||
{
|
||||
case F_TIMESTAMPTZ_LE:
|
||||
case F_TIMESTAMPTZ_LT:
|
||||
add = true;
|
||||
break;
|
||||
case F_TIMESTAMPTZ_GE:
|
||||
case F_TIMESTAMPTZ_GT:
|
||||
add = false;
|
||||
break;
|
||||
default:
|
||||
return constraint;
|
||||
}
|
||||
/*
|
||||
* If Var is on wrong side reverse the direction.
|
||||
*/
|
||||
if (!var_on_left)
|
||||
add = !add;
|
||||
|
||||
/*
|
||||
* The safety buffer is chosen to be 4 hours because daylight saving time
|
||||
* changes seem to be in the range between -1 and 2 hours.
|
||||
*/
|
||||
if (add)
|
||||
constified_tstz += 4 * USECS_PER_HOUR;
|
||||
else
|
||||
constified_tstz -= 4 * USECS_PER_HOUR;
|
||||
|
||||
constified = TimestampTzGetDatum(constified_tstz);
|
||||
}
|
||||
|
||||
c_ts = copyObject(c_ts);
|
||||
c_ts->constvalue = constified;
|
||||
|
||||
|
@ -246,3 +246,89 @@ QUERY PLAN
|
||||
One-Time Filter: false
|
||||
(2 rows)
|
||||
|
||||
-- test timezone changes in prepared statements
|
||||
CREATE TABLE dst_test(time timestamptz NOT NULL);
|
||||
SELECT table_name FROM create_hypertable('dst_test','time',chunk_time_interval:=interval '30 minutes');
|
||||
table_name
|
||||
dst_test
|
||||
(1 row)
|
||||
|
||||
INSERT INTO dst_test SELECT generate_series('2022-03-27 22:00:00+01'::timestamptz,'2022-03-28 02:00:00+01'::timestamptz,'30min'::interval);
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE time > '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
EXECUTE p1;
|
||||
count
|
||||
2
|
||||
(1 row)
|
||||
|
||||
set timezone TO 'Europe/Berlin';
|
||||
EXECUTE p1;
|
||||
count
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM dst_test WHERE time > '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
count
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DEALLOCATE p1;
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE time < '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
EXECUTE p1;
|
||||
count
|
||||
6
|
||||
(1 row)
|
||||
|
||||
set timezone TO 'Africa/Casablanca';
|
||||
EXECUTE p1;
|
||||
count
|
||||
8
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM dst_test WHERE time < '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
count
|
||||
8
|
||||
(1 row)
|
||||
|
||||
DEALLOCATE p1;
|
||||
-- do same tests with var on wrong side
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' < time;
|
||||
EXECUTE p1;
|
||||
count
|
||||
2
|
||||
(1 row)
|
||||
|
||||
set timezone TO 'Europe/Berlin';
|
||||
EXECUTE p1;
|
||||
count
|
||||
4
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' < time;
|
||||
count
|
||||
4
|
||||
(1 row)
|
||||
|
||||
DEALLOCATE p1;
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' > time;
|
||||
EXECUTE p1;
|
||||
count
|
||||
6
|
||||
(1 row)
|
||||
|
||||
set timezone TO 'Africa/Casablanca';
|
||||
EXECUTE p1;
|
||||
count
|
||||
8
|
||||
(1 row)
|
||||
|
||||
SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' > time;
|
||||
count
|
||||
8
|
||||
(1 row)
|
||||
|
||||
DEALLOCATE p1;
|
||||
DROP TABLE dst_test;
|
||||
|
@ -128,3 +128,45 @@ SELECT time
|
||||
FROM metrics
|
||||
WHERE time < NULL::timestamptz - NULL::interval;
|
||||
|
||||
-- test timezone changes in prepared statements
|
||||
CREATE TABLE dst_test(time timestamptz NOT NULL);
|
||||
SELECT table_name FROM create_hypertable('dst_test','time',chunk_time_interval:=interval '30 minutes');
|
||||
|
||||
INSERT INTO dst_test SELECT generate_series('2022-03-27 22:00:00+01'::timestamptz,'2022-03-28 02:00:00+01'::timestamptz,'30min'::interval);
|
||||
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE time > '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
EXECUTE p1;
|
||||
set timezone TO 'Europe/Berlin';
|
||||
EXECUTE p1;
|
||||
SELECT count(*) FROM dst_test WHERE time > '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
DEALLOCATE p1;
|
||||
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE time < '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
EXECUTE p1;
|
||||
set timezone TO 'Africa/Casablanca';
|
||||
EXECUTE p1;
|
||||
SELECT count(*) FROM dst_test WHERE time < '2022-03-27 00:00:00+00'::timestamptz + interval '1 day';
|
||||
DEALLOCATE p1;
|
||||
|
||||
-- do same tests with var on wrong side
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' < time;
|
||||
EXECUTE p1;
|
||||
set timezone TO 'Europe/Berlin';
|
||||
EXECUTE p1;
|
||||
SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' < time;
|
||||
DEALLOCATE p1;
|
||||
|
||||
SET timezone TO UTC;
|
||||
PREPARE p1 AS SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' > time;
|
||||
EXECUTE p1;
|
||||
set timezone TO 'Africa/Casablanca';
|
||||
EXECUTE p1;
|
||||
SELECT count(*) FROM dst_test WHERE '2022-03-27 00:00:00+00'::timestamptz + interval '1 day' > time;
|
||||
DEALLOCATE p1;
|
||||
|
||||
|
||||
DROP TABLE dst_test;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user