Fix time_bucket comparison transformation

The time_bucket comparison transformation code assumed the value and
the width of the time_bucket comparison expression were both Const.
But this was not validated only asserted. This can lead to wrong
query results. Found by sqlsmith.
This commit is contained in:
Sven Klemm 2021-11-08 15:00:29 +01:00 committed by Sven Klemm
parent 0f5268ad49
commit 7591f7f1f3
6 changed files with 130 additions and 8 deletions

View File

@ -11,8 +11,9 @@ accidentally triggering the load of a previous DB version.**
* #3769 Allow ALTER TABLE DROP COLUMN on compressed hypertable * #3769 Allow ALTER TABLE DROP COLUMN on compressed hypertable
**Bugfixes** **Bugfixes**
* #3766 Fix segfault in ts_hist_sfunc
* #3739 Fix compression policy on tables using INTEGER * #3739 Fix compression policy on tables using INTEGER
* #3766 Fix segfault in ts_hist_sfunc
* #3789 Fix time_bucket comparison transformation
**Thanks** **Thanks**
* @cbisnett for reporting and fixing a typo in an error message * @cbisnett for reporting and fixing a typo in an error message

View File

@ -318,8 +318,8 @@ transform_time_bucket_comparison(PlannerInfo *root, OpExpr *op)
TypeCacheEntry *tce; TypeCacheEntry *tce;
int strategy; int strategy;
/* caller must ensure time_bucket only has 2 arguments */ if (list_length(time_bucket->args) != 2 || !IsA(value, Const) || !IsA(width, Const))
Assert(list_length(time_bucket->args) == 2); return op;
/* /*
* if time_bucket call is on wrong side we switch operator * if time_bucket call is on wrong side we switch operator
@ -359,11 +359,6 @@ transform_time_bucket_comparison(PlannerInfo *root, OpExpr *op)
Datum datum; Datum datum;
int64 integralValue, integralWidth; int64 integralValue, integralWidth;
/*
* caller should make sure value and width are Const
*/
Assert(IsA(value, Const) && IsA(width, Const));
if (castNode(Const, value)->constisnull || width->constisnull) if (castNode(Const, value)->constisnull || width->constisnull)
return op; return op;

View File

@ -1391,6 +1391,47 @@ time_bucket exclusion with timestamptz and day interval
Filter: ((time_bucket('@ 1 day'::interval, "time") >= 'Mon Jan 03 00:00:00 2000 PST'::timestamp with time zone) AND (time_bucket('@ 7 days'::interval, "time") <= 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: ((time_bucket('@ 1 day'::interval, "time") >= 'Mon Jan 03 00:00:00 2000 PST'::timestamp with time zone) AND (time_bucket('@ 7 days'::interval, "time") <= 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone))
(11 rows) (11 rows)
\qecho no transformation
no transformation
:PREFIX SELECT * FROM hyper WHERE time_bucket(10 + floor(random())::int, time) > 10 AND time_bucket(10 + floor(random())::int, time) < 100 AND time < 150 ORDER BY time;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: hyper."time"
-> Custom Scan (ChunkAppend) on hyper
Chunks excluded during startup: 0
-> Seq Scan on _hyper_1_1_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_2_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_3_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_4_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_5_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_6_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_7_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_8_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_9_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_10_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_11_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_12_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_13_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_14_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_15_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
(34 rows)
\qecho exclude chunks based on time column with partitioning function. This \qecho exclude chunks based on time column with partitioning function. This
exclude chunks based on time column with partitioning function. This exclude chunks based on time column with partitioning function. This
\qecho transparently applies the time partitioning function on the time \qecho transparently applies the time partitioning function on the time

View File

@ -1391,6 +1391,47 @@ time_bucket exclusion with timestamptz and day interval
Filter: ((time_bucket('@ 1 day'::interval, "time") >= 'Mon Jan 03 00:00:00 2000 PST'::timestamp with time zone) AND (time_bucket('@ 7 days'::interval, "time") <= 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: ((time_bucket('@ 1 day'::interval, "time") >= 'Mon Jan 03 00:00:00 2000 PST'::timestamp with time zone) AND (time_bucket('@ 7 days'::interval, "time") <= 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone))
(11 rows) (11 rows)
\qecho no transformation
no transformation
:PREFIX SELECT * FROM hyper WHERE time_bucket(10 + floor(random())::int, time) > 10 AND time_bucket(10 + floor(random())::int, time) < 100 AND time < 150 ORDER BY time;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: hyper."time"
-> Custom Scan (ChunkAppend) on hyper
Chunks excluded during startup: 0
-> Seq Scan on _hyper_1_1_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_2_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_3_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_4_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_5_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_6_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_7_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_8_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_9_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_10_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_11_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_12_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_13_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_14_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_15_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
(34 rows)
\qecho exclude chunks based on time column with partitioning function. This \qecho exclude chunks based on time column with partitioning function. This
exclude chunks based on time column with partitioning function. This exclude chunks based on time column with partitioning function. This
\qecho transparently applies the time partitioning function on the time \qecho transparently applies the time partitioning function on the time

View File

@ -1391,6 +1391,47 @@ time_bucket exclusion with timestamptz and day interval
Filter: ((time_bucket('@ 1 day'::interval, "time") >= 'Mon Jan 03 00:00:00 2000 PST'::timestamp with time zone) AND (time_bucket('@ 7 days'::interval, "time") <= 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone)) Filter: ((time_bucket('@ 1 day'::interval, "time") >= 'Mon Jan 03 00:00:00 2000 PST'::timestamp with time zone) AND (time_bucket('@ 7 days'::interval, "time") <= 'Mon Jan 10 00:00:00 2000 PST'::timestamp with time zone))
(11 rows) (11 rows)
\qecho no transformation
no transformation
:PREFIX SELECT * FROM hyper WHERE time_bucket(10 + floor(random())::int, time) > 10 AND time_bucket(10 + floor(random())::int, time) < 100 AND time < 150 ORDER BY time;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: hyper."time"
-> Custom Scan (ChunkAppend) on hyper
Chunks excluded during startup: 0
-> Seq Scan on _hyper_1_1_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_2_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_3_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_4_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_5_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_6_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_7_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_8_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_9_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_10_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_11_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_12_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_13_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_14_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
-> Seq Scan on _hyper_1_15_chunk
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
(34 rows)
\qecho exclude chunks based on time column with partitioning function. This \qecho exclude chunks based on time column with partitioning function. This
exclude chunks based on time column with partitioning function. This exclude chunks based on time column with partitioning function. This
\qecho transparently applies the time partitioning function on the time \qecho transparently applies the time partitioning function on the time

View File

@ -202,6 +202,9 @@ SELECT * FROM cte ORDER BY value;
:PREFIX SELECT time FROM metrics_timestamptz WHERE time_bucket('1d',time) >= '2000-01-03' AND time_bucket('1d',time) <= '2000-01-10' ORDER BY time; :PREFIX SELECT time FROM metrics_timestamptz WHERE time_bucket('1d',time) >= '2000-01-03' AND time_bucket('1d',time) <= '2000-01-10' ORDER BY time;
:PREFIX SELECT time FROM metrics_timestamptz WHERE time_bucket('1d',time) >= '2000-01-03' AND time_bucket('7d',time) <= '2000-01-10' ORDER BY time; :PREFIX SELECT time FROM metrics_timestamptz WHERE time_bucket('1d',time) >= '2000-01-03' AND time_bucket('7d',time) <= '2000-01-10' ORDER BY time;
\qecho no transformation
:PREFIX SELECT * FROM hyper WHERE time_bucket(10 + floor(random())::int, time) > 10 AND time_bucket(10 + floor(random())::int, time) < 100 AND time < 150 ORDER BY time;
\qecho exclude chunks based on time column with partitioning function. This \qecho exclude chunks based on time column with partitioning function. This
\qecho transparently applies the time partitioning function on the time \qecho transparently applies the time partitioning function on the time
\qecho value to be able to exclude chunks (similar to a closed dimension). \qecho value to be able to exclude chunks (similar to a closed dimension).