From 1cd77b3a24169f7faad75c72145ce44383d855a0 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov <36882414+akuzm@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:50:02 +0100 Subject: [PATCH] Vectorize filters with external parameters For example, comparison to a query parameter in a parameterized prepared query. --- tsl/src/nodes/decompress_chunk/planner.c | 21 ++++-- tsl/test/expected/decompress_vector_qual.out | 48 +++++++++++++ .../constraint_exclusion_prepared.out | 72 +++++++++---------- tsl/test/sql/decompress_vector_qual.sql | 31 ++++++++ 4 files changed, 130 insertions(+), 42 deletions(-) diff --git a/tsl/src/nodes/decompress_chunk/planner.c b/tsl/src/nodes/decompress_chunk/planner.c index f12bc4ebf..0c3ce7fc8 100644 --- a/tsl/src/nodes/decompress_chunk/planner.c +++ b/tsl/src/nodes/decompress_chunk/planner.c @@ -373,15 +373,24 @@ is_not_runtime_constant_walker(Node *node, void *context) { case T_Var: case T_PlaceHolderVar: - case T_Param: /* - * We might want to support these nodes to have vectorizable - * join clauses (T_Var), join clauses referencing a variable that is - * above outer join (T_PlaceHolderVar) or initplan parameters and - * prepared statement parameters (T_Param). We don't support them at - * the moment. + * We might want to support these nodes to have vectorizable join + * clauses (T_Var) or join clauses referencing a variable that is + * above outer join (T_PlaceHolderVar). We don't suppor them at the + * moment. */ return true; + case T_Param: + /* + * We support external query parameters (e.g. from parameterized + * prepared statements), because they are constant for the duration + * of the query. + * + * Join and initplan parameters are passed as PARAM_EXEC and require + * support in the Rescan functions of the custom scan node. We don't + * support them at the moment. + */ + return castNode(Param, node)->paramkind != PARAM_EXTERN; default: if (check_functions_in_node(node, contains_volatile_functions_checker, diff --git a/tsl/test/expected/decompress_vector_qual.out b/tsl/test/expected/decompress_vector_qual.out index 574231790..8697be9f0 100644 --- a/tsl/test/expected/decompress_vector_qual.out +++ b/tsl/test/expected/decompress_vector_qual.out @@ -132,7 +132,55 @@ select tag from vectorqual where metric2 > 0; tag5 (5 rows) +-- Can't vectorize parameterized join clauses for now. +set timescaledb.debug_require_vector_qual to 'forbid'; +set enable_hashjoin to off; +set enable_mergejoin to off; +with values(x) as materialized(select distinct metric2 from vectorqual) + select x, (select metric2 from vectorqual where metric2 = x) from values order by 1; + x | metric2 +----+--------- + 12 | 12 + 22 | 22 + 32 | 32 + 42 | 42 + 52 | 52 +(5 rows) + +reset enable_hashjoin; +reset enable_mergejoin; +-- Can't vectorize initplan parameters either. +select count(*) from vectorqual where metric2 + = (select metric2 from vectorqual order by 1 limit 1); + count +------- + 1 +(1 row) + +-- Can vectorize clauses with query parameters. +set timescaledb.debug_require_vector_qual to 'only'; +set plan_cache_mode to 'force_generic_plan'; +prepare p as select count(*) from vectorqual where metric3 = $1; +execute p(33); + count +------- + 1 +(1 row) + +deallocate p; +-- Also try query parameter in combination with a stable function. +create function stable_identity(x anyelement) returns anyelement as $$ select x $$ language sql stable; +prepare p(int4) as select count(*) from vectorqual where metric3 = stable_identity($1); +execute p(33); + count +------- + 1 +(1 row) + +deallocate p; +reset plan_cache_mode; -- Queries without aggregation. +set timescaledb.debug_require_vector_qual to 'only'; select * from vectorqual where ts > '2021-01-01 00:00:00' order by vectorqual; ts | metric2 | device | metric3 | metric4 | tag --------------------------+---------+--------+---------+---------+------ diff --git a/tsl/test/shared/expected/constraint_exclusion_prepared.out b/tsl/test/shared/expected/constraint_exclusion_prepared.out index c7afcbe31..9842725e1 100644 --- a/tsl/test/shared/expected/constraint_exclusion_prepared.out +++ b/tsl/test/shared/expected/constraint_exclusion_prepared.out @@ -2124,17 +2124,17 @@ QUERY PLAN Custom Scan (ChunkAppend) on metrics_compressed (actual rows=5 loops=1) Chunks excluded during runtime: 2 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=5 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 4995 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=5 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 15 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) (16 rows) @@ -2144,17 +2144,17 @@ QUERY PLAN Custom Scan (ChunkAppend) on metrics_compressed (actual rows=5 loops=1) Chunks excluded during runtime: 2 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=5 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 4995 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=5 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 25 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) (16 rows) @@ -2164,15 +2164,15 @@ QUERY PLAN Custom Scan (ChunkAppend) on metrics_compressed (actual rows=0 loops=1) Chunks excluded during runtime: 3 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) (14 rows) @@ -3407,45 +3407,45 @@ QUERY PLAN Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=5 loops=1) Chunks excluded during runtime: 6 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=1 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 999 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=1 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 3 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=3 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 2997 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=3 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 9 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=1 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 999 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=1 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 3 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) (44 rows) @@ -3455,45 +3455,45 @@ QUERY PLAN Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=5 loops=1) Chunks excluded during runtime: 6 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=1 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 999 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=1 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 5 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=3 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 2997 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=3 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 15 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (actual rows=1 loops=1) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) Rows Removed by Filter: 999 -> Seq Scan on compress_hyper_X_X_chunk (actual rows=1 loops=1) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) Rows Removed by Filter: 5 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) (44 rows) @@ -3503,39 +3503,39 @@ QUERY PLAN Custom Scan (ChunkAppend) on metrics_space_compressed (actual rows=0 loops=1) Chunks excluded during runtime: 9 -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) -> Custom Scan (DecompressChunk) on _hyper_X_X_chunk (never executed) - Filter: ("time" = $1) + Vectorized Filter: ("time" = $1) -> Seq Scan on compress_hyper_X_X_chunk (never executed) Filter: ((_ts_meta_min_1 <= $1) AND (_ts_meta_max_1 >= $1)) (38 rows) diff --git a/tsl/test/sql/decompress_vector_qual.sql b/tsl/test/sql/decompress_vector_qual.sql index 04a5da1c3..f5aa0e5ff 100644 --- a/tsl/test/sql/decompress_vector_qual.sql +++ b/tsl/test/sql/decompress_vector_qual.sql @@ -47,7 +47,38 @@ set timescaledb.debug_require_vector_qual to 'only'; select tag from vectorqual where metric2 > 0; +-- Can't vectorize parameterized join clauses for now. +set timescaledb.debug_require_vector_qual to 'forbid'; +set enable_hashjoin to off; +set enable_mergejoin to off; +with values(x) as materialized(select distinct metric2 from vectorqual) + select x, (select metric2 from vectorqual where metric2 = x) from values order by 1; +reset enable_hashjoin; +reset enable_mergejoin; + +-- Can't vectorize initplan parameters either. +select count(*) from vectorqual where metric2 + = (select metric2 from vectorqual order by 1 limit 1); + +-- Can vectorize clauses with query parameters. +set timescaledb.debug_require_vector_qual to 'only'; +set plan_cache_mode to 'force_generic_plan'; + +prepare p as select count(*) from vectorqual where metric3 = $1; +execute p(33); +deallocate p; + +-- Also try query parameter in combination with a stable function. +create function stable_identity(x anyelement) returns anyelement as $$ select x $$ language sql stable; +prepare p(int4) as select count(*) from vectorqual where metric3 = stable_identity($1); +execute p(33); +deallocate p; + +reset plan_cache_mode; + + -- Queries without aggregation. +set timescaledb.debug_require_vector_qual to 'only'; select * from vectorqual where ts > '2021-01-01 00:00:00' order by vectorqual; select * from vectorqual where metric4 >= 0 order by vectorqual;