From 180c7be7bd9a790111f0ab2bceefe4dec719cb7d Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Sun, 26 May 2024 13:56:44 +0200 Subject: [PATCH] Fix segfault in UNION queries with top-level ordering We can't just filter the equivalence member based on em_is_child as our hypertable might not be top-level equivalence member. --- .unreleased/pr_6957 | 1 + .../nodes/decompress_chunk/decompress_chunk.c | 20 +++++++--------- .../transparent_decompress_chunk-14.out | 24 +++++++++++++++++++ .../transparent_decompress_chunk-15.out | 24 +++++++++++++++++++ .../transparent_decompress_chunk-16.out | 24 +++++++++++++++++++ .../sql/transparent_decompress_chunk.sql.in | 15 ++++++++++++ 6 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 .unreleased/pr_6957 diff --git a/.unreleased/pr_6957 b/.unreleased/pr_6957 new file mode 100644 index 000000000..b213f137e --- /dev/null +++ b/.unreleased/pr_6957 @@ -0,0 +1 @@ +Fixes: #6957 Fix segfault in UNION queries with ordering on compressed chunks diff --git a/tsl/src/nodes/decompress_chunk/decompress_chunk.c b/tsl/src/nodes/decompress_chunk/decompress_chunk.c index 009910f3e..e98f7a521 100644 --- a/tsl/src/nodes/decompress_chunk/decompress_chunk.c +++ b/tsl/src/nodes/decompress_chunk/decompress_chunk.c @@ -195,7 +195,7 @@ build_compressed_scan_pathkeys(SortInfo *sort_info, PlannerInfo *root, List *chu * already refers a compressed column, it is a bug. See * build_sortinfo(). */ - Assert(compressed_em != NULL); + Ensure(compressed_em, "corresponding equivalence member not found"); required_compressed_pathkeys = lappend(required_compressed_pathkeys, pk); @@ -1482,17 +1482,6 @@ add_segmentby_to_equivalence_class(PlannerInfo *root, EquivalenceClass *cur_ec, Var *var; Assert(!bms_overlap(cur_em->em_relids, info->compressed_rel->relids)); - /* - * We want to base our equivalence member on the hypertable equivalence - * member, not on the uncompressed chunk one, because the latter is - * marked as child itself. This is mostly relevant for PG16 where we - * have to specify a parent for the newly created equivalence member. - */ - if (cur_em->em_is_child) - { - continue; - } - /* only consider EquivalenceMembers that are Vars, possibly with RelabelType, of the * uncompressed chunk */ var = (Var *) cur_em->em_expr; @@ -1501,6 +1490,13 @@ add_segmentby_to_equivalence_class(PlannerInfo *root, EquivalenceClass *cur_ec, if (!(var && IsA(var, Var))) continue; + /* + * We want to base our equivalence member on the hypertable equivalence + * member, not on the uncompressed chunk one. We can't just check for + * em_is_child though because the hypertable might be a child itself and not + * a top-level EquivalenceMember. This is mostly relevant for PG16+ where + * we have to specify a parent for the newly created equivalence member. + */ if ((Index) var->varno != info->ht_rel->relid) continue; diff --git a/tsl/test/shared/expected/transparent_decompress_chunk-14.out b/tsl/test/shared/expected/transparent_decompress_chunk-14.out index e69fc4d0f..9177b71e2 100644 --- a/tsl/test/shared/expected/transparent_decompress_chunk-14.out +++ b/tsl/test/shared/expected/transparent_decompress_chunk-14.out @@ -1040,3 +1040,27 @@ EXECUTE param_prep (1); DEALLOCATE param_prep; RESET plan_cache_mode; +-- test hypertable being non-toplevel equivalence member #6925 +CREATE TABLE i6925_t1(observed timestamptz not null, queryid int8, total_exec_time int8); +CREATE TABLE i6925_t2(LIKE i6925_t1); +SELECT table_name FROM create_hypertable('i6925_t1', 'observed'); + table_name + i6925_t1 +(1 row) + +ALTER TABLE i6925_t1 SET (timescaledb.compress, timescaledb.compress_segmentby = 'queryid'); +NOTICE: default order by for hypertable "i6925_t1" is set to "observed DESC" +INSERT INTO i6925_t1 SELECT '2020-01-01', 1, 1; +SELECT count(compress_chunk(chunk_name)) FROM show_chunks('i6925_t1') chunk_name; + count + 1 +(1 row) + +SELECT queryid, lag(total_exec_time) OVER (PARTITION BY queryid) FROM (SELECT * FROM i6925_t1 UNION ALL SELECT * FROM i6925_t2) q; + queryid | lag +---------+----- + 1 | +(1 row) + +DROP TABLE i6925_t1; +DROP TABLE i6925_t2; diff --git a/tsl/test/shared/expected/transparent_decompress_chunk-15.out b/tsl/test/shared/expected/transparent_decompress_chunk-15.out index b964e8363..0715dae5b 100644 --- a/tsl/test/shared/expected/transparent_decompress_chunk-15.out +++ b/tsl/test/shared/expected/transparent_decompress_chunk-15.out @@ -1042,3 +1042,27 @@ EXECUTE param_prep (1); DEALLOCATE param_prep; RESET plan_cache_mode; +-- test hypertable being non-toplevel equivalence member #6925 +CREATE TABLE i6925_t1(observed timestamptz not null, queryid int8, total_exec_time int8); +CREATE TABLE i6925_t2(LIKE i6925_t1); +SELECT table_name FROM create_hypertable('i6925_t1', 'observed'); + table_name + i6925_t1 +(1 row) + +ALTER TABLE i6925_t1 SET (timescaledb.compress, timescaledb.compress_segmentby = 'queryid'); +NOTICE: default order by for hypertable "i6925_t1" is set to "observed DESC" +INSERT INTO i6925_t1 SELECT '2020-01-01', 1, 1; +SELECT count(compress_chunk(chunk_name)) FROM show_chunks('i6925_t1') chunk_name; + count + 1 +(1 row) + +SELECT queryid, lag(total_exec_time) OVER (PARTITION BY queryid) FROM (SELECT * FROM i6925_t1 UNION ALL SELECT * FROM i6925_t2) q; + queryid | lag +---------+----- + 1 | +(1 row) + +DROP TABLE i6925_t1; +DROP TABLE i6925_t2; diff --git a/tsl/test/shared/expected/transparent_decompress_chunk-16.out b/tsl/test/shared/expected/transparent_decompress_chunk-16.out index 7f650ca53..ef70092f3 100644 --- a/tsl/test/shared/expected/transparent_decompress_chunk-16.out +++ b/tsl/test/shared/expected/transparent_decompress_chunk-16.out @@ -1042,3 +1042,27 @@ EXECUTE param_prep (1); DEALLOCATE param_prep; RESET plan_cache_mode; +-- test hypertable being non-toplevel equivalence member #6925 +CREATE TABLE i6925_t1(observed timestamptz not null, queryid int8, total_exec_time int8); +CREATE TABLE i6925_t2(LIKE i6925_t1); +SELECT table_name FROM create_hypertable('i6925_t1', 'observed'); + table_name + i6925_t1 +(1 row) + +ALTER TABLE i6925_t1 SET (timescaledb.compress, timescaledb.compress_segmentby = 'queryid'); +NOTICE: default order by for hypertable "i6925_t1" is set to "observed DESC" +INSERT INTO i6925_t1 SELECT '2020-01-01', 1, 1; +SELECT count(compress_chunk(chunk_name)) FROM show_chunks('i6925_t1') chunk_name; + count + 1 +(1 row) + +SELECT queryid, lag(total_exec_time) OVER (PARTITION BY queryid) FROM (SELECT * FROM i6925_t1 UNION ALL SELECT * FROM i6925_t2) q; + queryid | lag +---------+----- + 1 | +(1 row) + +DROP TABLE i6925_t1; +DROP TABLE i6925_t2; diff --git a/tsl/test/shared/sql/transparent_decompress_chunk.sql.in b/tsl/test/shared/sql/transparent_decompress_chunk.sql.in index a1170abf1..93fdac211 100644 --- a/tsl/test/shared/sql/transparent_decompress_chunk.sql.in +++ b/tsl/test/shared/sql/transparent_decompress_chunk.sql.in @@ -331,3 +331,18 @@ EXECUTE param_prep (2); EXECUTE param_prep (1); DEALLOCATE param_prep; RESET plan_cache_mode; + +-- test hypertable being non-toplevel equivalence member #6925 +CREATE TABLE i6925_t1(observed timestamptz not null, queryid int8, total_exec_time int8); +CREATE TABLE i6925_t2(LIKE i6925_t1); + +SELECT table_name FROM create_hypertable('i6925_t1', 'observed'); +ALTER TABLE i6925_t1 SET (timescaledb.compress, timescaledb.compress_segmentby = 'queryid'); +INSERT INTO i6925_t1 SELECT '2020-01-01', 1, 1; +SELECT count(compress_chunk(chunk_name)) FROM show_chunks('i6925_t1') chunk_name; + +SELECT queryid, lag(total_exec_time) OVER (PARTITION BY queryid) FROM (SELECT * FROM i6925_t1 UNION ALL SELECT * FROM i6925_t2) q; + +DROP TABLE i6925_t1; +DROP TABLE i6925_t2; +