From 301612af550ac48a6513aefc0ed7deef6bb014f4 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 18 Dec 2023 15:50:54 +0100 Subject: [PATCH] Allow SQLValueFunction pushdown into compressed scan Since all SQLValueFunction are STABLE we can safely push them down into the scan of the compressed chunk. The following expressions are pushed down: - CURRENT_DATE - CURRENT_TIME - CURRENT_TIME(p) - CURRENT_TIMESTAMP - CURRENT_TIMESTAMP(p) - LOCALTIME - LOCALTIME(p) - LOCALTIMESTAMP - LOCALTIMESTAMP(p) - USER - CURRENT_USER - SESSION_USER - CURRENT_CATALOG - CURRENT_SCHEMA --- .unreleased/pr_6440 | 1 + .../nodes/decompress_chunk/qual_pushdown.c | 1 + .../expected/compression_qualpushdown.out | 169 ++++++++++++++++++ tsl/test/expected/decompress_vector_qual.out | 14 ++ tsl/test/sql/compression_qualpushdown.sql | 28 +++ tsl/test/sql/decompress_vector_qual.sql | 3 + 6 files changed, 216 insertions(+) create mode 100644 .unreleased/pr_6440 diff --git a/.unreleased/pr_6440 b/.unreleased/pr_6440 new file mode 100644 index 000000000..f84c25766 --- /dev/null +++ b/.unreleased/pr_6440 @@ -0,0 +1 @@ +Implements: #6440 Allow SQLValueFunction pushdown into compressed scan diff --git a/tsl/src/nodes/decompress_chunk/qual_pushdown.c b/tsl/src/nodes/decompress_chunk/qual_pushdown.c index 278bc464f..005e8fcf8 100644 --- a/tsl/src/nodes/decompress_chunk/qual_pushdown.c +++ b/tsl/src/nodes/decompress_chunk/qual_pushdown.c @@ -354,6 +354,7 @@ modify_expression(Node *node, QualPushdownContext *context) case T_Const: case T_NullTest: case T_Param: + case T_SQLValueFunction: break; case T_Var: { diff --git a/tsl/test/expected/compression_qualpushdown.out b/tsl/test/expected/compression_qualpushdown.out index 7219e0acc..276ec29b5 100644 --- a/tsl/test/expected/compression_qualpushdown.out +++ b/tsl/test/expected/compression_qualpushdown.out @@ -345,3 +345,172 @@ EXPLAIN (costs off) SELECT '1' FROM deleteme_with_bytea WHERE bdata::text = '123 DROP table deleteme; DROP table deleteme_with_bytea; +-- test sqlvaluefunction pushdown +CREATE TABLE svf_pushdown(time timestamptz, c_date date, c_time time, c_timetz timetz, c_timestamp timestamptz, c_name text); +SELECT table_name FROM create_hypertable('svf_pushdown', 'time'); +NOTICE: adding not-null constraint to column "time" + table_name +-------------- + svf_pushdown +(1 row) + +ALTER TABLE svf_pushdown SET (timescaledb.compress,timescaledb.compress_segmentby='c_date,c_time, c_timetz,c_timestamp,c_name'); +INSERT INTO svf_pushdown SELECT '2020-01-01'; +SELECT compress_chunk(show_chunks('svf_pushdown')); + compress_chunk +------------------------------------------ + _timescaledb_internal._hyper_11_12_chunk +(1 row) + +-- constraints should be pushed down into scan below decompresschunk in all cases +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_date = CURRENT_DATE; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_date = CURRENT_DATE) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timetz = CURRENT_TIME; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_timetz = CURRENT_TIME) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timetz = CURRENT_TIME(1); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_timetz = CURRENT_TIME(1)) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = CURRENT_TIMESTAMP; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_timestamp = CURRENT_TIMESTAMP) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = CURRENT_TIMESTAMP(1); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_timestamp = CURRENT_TIMESTAMP(1)) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_time = LOCALTIME; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_time = LOCALTIME) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_time = LOCALTIME(1); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_time = LOCALTIME(1)) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = LOCALTIMESTAMP; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_timestamp = LOCALTIMESTAMP) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = LOCALTIMESTAMP(1); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Index Scan using compress_hyper_12_13_chunk__compressed_hypertable_12_c_date_c_t on compress_hyper_12_13_chunk + Index Cond: (c_timestamp = LOCALTIMESTAMP(1)) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = USER; + QUERY PLAN +----------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Seq Scan on compress_hyper_12_13_chunk + Filter: (c_name = USER) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = CURRENT_USER; + QUERY PLAN +----------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Seq Scan on compress_hyper_12_13_chunk + Filter: (c_name = CURRENT_USER) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = SESSION_USER; + QUERY PLAN +----------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Seq Scan on compress_hyper_12_13_chunk + Filter: (c_name = SESSION_USER) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = CURRENT_CATALOG; + QUERY PLAN +----------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Seq Scan on compress_hyper_12_13_chunk + Filter: (c_name = CURRENT_CATALOG) +(5 rows) + +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = CURRENT_SCHEMA; + QUERY PLAN +----------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + -> Seq Scan on compress_hyper_12_13_chunk + Filter: (c_name = CURRENT_SCHEMA) +(5 rows) + +-- current_query() is not a sqlvaluefunction and volatile so should not be pushed down +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = current_query(); + QUERY PLAN +----------------------------------------------------------- + Custom Scan (ChunkAppend) on svf_pushdown + Chunks excluded during startup: 0 + -> Custom Scan (DecompressChunk) on _hyper_11_12_chunk + Filter: (c_name = current_query()) + -> Seq Scan on compress_hyper_12_13_chunk +(5 rows) + diff --git a/tsl/test/expected/decompress_vector_qual.out b/tsl/test/expected/decompress_vector_qual.out index 4ccf378e1..596245cba 100644 --- a/tsl/test/expected/decompress_vector_qual.out +++ b/tsl/test/expected/decompress_vector_qual.out @@ -569,6 +569,12 @@ select count(*) from vectorqual where ts > case when '2021-01-01'::timestamp < ' 0 (1 row) +select count(*) from vectorqual where ts < LOCALTIMESTAMP + '3 years'::interval; + count +------- + 5 +(1 row) + -- This filter is not vectorized because the 'timestamp > timestamptz' -- operator is stable, not immutable, because it uses the current session -- timezone. We could transform it to something like @@ -664,3 +670,11 @@ select * from date_table where ts < '2021-01-02'; 01-01-2021 (1 row) +select * from date_table where ts < CURRENT_DATE; + ts +------------ + 01-03-2021 + 01-02-2021 + 01-01-2021 +(3 rows) + diff --git a/tsl/test/sql/compression_qualpushdown.sql b/tsl/test/sql/compression_qualpushdown.sql index 531ac6ac4..0c29df5d9 100644 --- a/tsl/test/sql/compression_qualpushdown.sql +++ b/tsl/test/sql/compression_qualpushdown.sql @@ -153,3 +153,31 @@ EXPLAIN (costs off) SELECT '1' FROM deleteme_with_bytea WHERE bdata::text = '123 DROP table deleteme; DROP table deleteme_with_bytea; + +-- test sqlvaluefunction pushdown +CREATE TABLE svf_pushdown(time timestamptz, c_date date, c_time time, c_timetz timetz, c_timestamp timestamptz, c_name text); +SELECT table_name FROM create_hypertable('svf_pushdown', 'time'); +ALTER TABLE svf_pushdown SET (timescaledb.compress,timescaledb.compress_segmentby='c_date,c_time, c_timetz,c_timestamp,c_name'); + +INSERT INTO svf_pushdown SELECT '2020-01-01'; +SELECT compress_chunk(show_chunks('svf_pushdown')); + +-- constraints should be pushed down into scan below decompresschunk in all cases +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_date = CURRENT_DATE; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timetz = CURRENT_TIME; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timetz = CURRENT_TIME(1); +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = CURRENT_TIMESTAMP; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = CURRENT_TIMESTAMP(1); +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_time = LOCALTIME; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_time = LOCALTIME(1); +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = LOCALTIMESTAMP; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_timestamp = LOCALTIMESTAMP(1); +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = USER; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = CURRENT_USER; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = SESSION_USER; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = CURRENT_CATALOG; +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = CURRENT_SCHEMA; + +-- current_query() is not a sqlvaluefunction and volatile so should not be pushed down +EXPLAIN (costs off) SELECT * FROM svf_pushdown WHERE c_name = current_query(); + diff --git a/tsl/test/sql/decompress_vector_qual.sql b/tsl/test/sql/decompress_vector_qual.sql index 9cee66531..0b783303c 100644 --- a/tsl/test/sql/decompress_vector_qual.sql +++ b/tsl/test/sql/decompress_vector_qual.sql @@ -176,6 +176,7 @@ select count(*) from vectorqual where ts > '2021-01-01 00:00:00'::timestamptz::t select count(*) from vectorqual where ts > '2021-01-01 00:00:00'::timestamp - interval '1 day'; -- Expression that evaluates to Null. select count(*) from vectorqual where ts > case when '2021-01-01'::timestamp < '2022-01-01'::timestamptz then null else '2021-01-01 00:00:00'::timestamp end; +select count(*) from vectorqual where ts < LOCALTIMESTAMP + '3 years'::interval; -- This filter is not vectorized because the 'timestamp > timestamptz' -- operator is stable, not immutable, because it uses the current session @@ -220,3 +221,5 @@ select * from date_table where ts >= '2021-01-02'; select * from date_table where ts = '2021-01-02'; select * from date_table where ts <= '2021-01-02'; select * from date_table where ts < '2021-01-02'; +select * from date_table where ts < CURRENT_DATE; +