Skip to content

Commit 7591f7f

Browse files
committed
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.
1 parent 0f5268a commit 7591f7f

File tree

6 files changed

+130
-8
lines changed

6 files changed

+130
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ accidentally triggering the load of a previous DB version.**
1111
* #3769 Allow ALTER TABLE DROP COLUMN on compressed hypertable
1212

1313
**Bugfixes**
14-
* #3766 Fix segfault in ts_hist_sfunc
1514
* #3739 Fix compression policy on tables using INTEGER
15+
* #3766 Fix segfault in ts_hist_sfunc
16+
* #3789 Fix time_bucket comparison transformation
1617

1718
**Thanks**
1819
* @cbisnett for reporting and fixing a typo in an error message

src/plan_expand_hypertable.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ transform_time_bucket_comparison(PlannerInfo *root, OpExpr *op)
318318
TypeCacheEntry *tce;
319319
int strategy;
320320

321-
/* caller must ensure time_bucket only has 2 arguments */
322-
Assert(list_length(time_bucket->args) == 2);
321+
if (list_length(time_bucket->args) != 2 || !IsA(value, Const) || !IsA(width, Const))
322+
return op;
323323

324324
/*
325325
* if time_bucket call is on wrong side we switch operator
@@ -359,11 +359,6 @@ transform_time_bucket_comparison(PlannerInfo *root, OpExpr *op)
359359
Datum datum;
360360
int64 integralValue, integralWidth;
361361

362-
/*
363-
* caller should make sure value and width are Const
364-
*/
365-
Assert(IsA(value, Const) && IsA(width, Const));
366-
367362
if (castNode(Const, value)->constisnull || width->constisnull)
368363
return op;
369364

test/expected/plan_expand_hypertable-12.out

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,47 @@ time_bucket exclusion with timestamptz and day interval
13911391
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))
13921392
(11 rows)
13931393

1394+
\qecho no transformation
1395+
no transformation
1396+
: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;
1397+
QUERY PLAN
1398+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1399+
Sort
1400+
Sort Key: hyper."time"
1401+
-> Custom Scan (ChunkAppend) on hyper
1402+
Chunks excluded during startup: 0
1403+
-> Seq Scan on _hyper_1_1_chunk
1404+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1405+
-> Seq Scan on _hyper_1_2_chunk
1406+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1407+
-> Seq Scan on _hyper_1_3_chunk
1408+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1409+
-> Seq Scan on _hyper_1_4_chunk
1410+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1411+
-> Seq Scan on _hyper_1_5_chunk
1412+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1413+
-> Seq Scan on _hyper_1_6_chunk
1414+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1415+
-> Seq Scan on _hyper_1_7_chunk
1416+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1417+
-> Seq Scan on _hyper_1_8_chunk
1418+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1419+
-> Seq Scan on _hyper_1_9_chunk
1420+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1421+
-> Seq Scan on _hyper_1_10_chunk
1422+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1423+
-> Seq Scan on _hyper_1_11_chunk
1424+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1425+
-> Seq Scan on _hyper_1_12_chunk
1426+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1427+
-> Seq Scan on _hyper_1_13_chunk
1428+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1429+
-> Seq Scan on _hyper_1_14_chunk
1430+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1431+
-> Seq Scan on _hyper_1_15_chunk
1432+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1433+
(34 rows)
1434+
13941435
\qecho exclude chunks based on time column with partitioning function. This
13951436
exclude chunks based on time column with partitioning function. This
13961437
\qecho transparently applies the time partitioning function on the time

test/expected/plan_expand_hypertable-13.out

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,47 @@ time_bucket exclusion with timestamptz and day interval
13911391
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))
13921392
(11 rows)
13931393

1394+
\qecho no transformation
1395+
no transformation
1396+
: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;
1397+
QUERY PLAN
1398+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1399+
Sort
1400+
Sort Key: hyper."time"
1401+
-> Custom Scan (ChunkAppend) on hyper
1402+
Chunks excluded during startup: 0
1403+
-> Seq Scan on _hyper_1_1_chunk
1404+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1405+
-> Seq Scan on _hyper_1_2_chunk
1406+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1407+
-> Seq Scan on _hyper_1_3_chunk
1408+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1409+
-> Seq Scan on _hyper_1_4_chunk
1410+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1411+
-> Seq Scan on _hyper_1_5_chunk
1412+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1413+
-> Seq Scan on _hyper_1_6_chunk
1414+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1415+
-> Seq Scan on _hyper_1_7_chunk
1416+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1417+
-> Seq Scan on _hyper_1_8_chunk
1418+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1419+
-> Seq Scan on _hyper_1_9_chunk
1420+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1421+
-> Seq Scan on _hyper_1_10_chunk
1422+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1423+
-> Seq Scan on _hyper_1_11_chunk
1424+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1425+
-> Seq Scan on _hyper_1_12_chunk
1426+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1427+
-> Seq Scan on _hyper_1_13_chunk
1428+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1429+
-> Seq Scan on _hyper_1_14_chunk
1430+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1431+
-> Seq Scan on _hyper_1_15_chunk
1432+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1433+
(34 rows)
1434+
13941435
\qecho exclude chunks based on time column with partitioning function. This
13951436
exclude chunks based on time column with partitioning function. This
13961437
\qecho transparently applies the time partitioning function on the time

test/expected/plan_expand_hypertable-14.out

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,47 @@ time_bucket exclusion with timestamptz and day interval
13911391
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))
13921392
(11 rows)
13931393

1394+
\qecho no transformation
1395+
no transformation
1396+
: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;
1397+
QUERY PLAN
1398+
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1399+
Sort
1400+
Sort Key: hyper."time"
1401+
-> Custom Scan (ChunkAppend) on hyper
1402+
Chunks excluded during startup: 0
1403+
-> Seq Scan on _hyper_1_1_chunk
1404+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1405+
-> Seq Scan on _hyper_1_2_chunk
1406+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1407+
-> Seq Scan on _hyper_1_3_chunk
1408+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1409+
-> Seq Scan on _hyper_1_4_chunk
1410+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1411+
-> Seq Scan on _hyper_1_5_chunk
1412+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1413+
-> Seq Scan on _hyper_1_6_chunk
1414+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1415+
-> Seq Scan on _hyper_1_7_chunk
1416+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1417+
-> Seq Scan on _hyper_1_8_chunk
1418+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1419+
-> Seq Scan on _hyper_1_9_chunk
1420+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1421+
-> Seq Scan on _hyper_1_10_chunk
1422+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1423+
-> Seq Scan on _hyper_1_11_chunk
1424+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1425+
-> Seq Scan on _hyper_1_12_chunk
1426+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1427+
-> Seq Scan on _hyper_1_13_chunk
1428+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1429+
-> Seq Scan on _hyper_1_14_chunk
1430+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1431+
-> Seq Scan on _hyper_1_15_chunk
1432+
Filter: (("time" < 150) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") > 10) AND (time_bucket(((10 + (floor(random()))::integer))::bigint, "time") < 100))
1433+
(34 rows)
1434+
13941435
\qecho exclude chunks based on time column with partitioning function. This
13951436
exclude chunks based on time column with partitioning function. This
13961437
\qecho transparently applies the time partitioning function on the time

test/sql/include/plan_expand_hypertable_query.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ SELECT * FROM cte ORDER BY value;
202202
: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;
203203
: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;
204204

205+
\qecho no transformation
206+
: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;
207+
205208
\qecho exclude chunks based on time column with partitioning function. This
206209
\qecho transparently applies the time partitioning function on the time
207210
\qecho value to be able to exclude chunks (similar to a closed dimension).

0 commit comments

Comments
 (0)