From 40b3f753a21bfcb8f20609eaba2d535b3f00e3d4 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Thu, 27 Nov 2025 14:35:37 +0100 Subject: [PATCH] Don't ignore additional filters on same column when building scankeys This would lead to assertion failure in debug builds and ignore additional filter in non-debug builds. This would not lead to data corruption but it could lead to more data being decompressed than necessary for DML operations on compressed chunks. Found by sqlsmith (cherry picked from commit 91ce04d828d728151097cc506ea05514a4952c90) --- .unreleased/pr_8988 | 1 + tsl/src/compression/compression_scankey.c | 1 - tsl/test/shared/expected/compression_dml.out | 75 +++++++++++++++++++ .../transparent_decompress_chunk-15.out | 24 +++--- .../transparent_decompress_chunk-16.out | 24 +++--- .../transparent_decompress_chunk-17.out | 24 +++--- .../transparent_decompress_chunk-18.out | 24 +++--- tsl/test/shared/sql/compression_dml.sql | 10 +++ 8 files changed, 142 insertions(+), 41 deletions(-) create mode 100644 .unreleased/pr_8988 diff --git a/.unreleased/pr_8988 b/.unreleased/pr_8988 new file mode 100644 index 00000000000..a5e27c00f9d --- /dev/null +++ b/.unreleased/pr_8988 @@ -0,0 +1 @@ +Fixes: #8988 Don't ignore additional filters on same column when building scankeys diff --git a/tsl/src/compression/compression_scankey.c b/tsl/src/compression/compression_scankey.c index a10fc963268..4997c784280 100644 --- a/tsl/src/compression/compression_scankey.c +++ b/tsl/src/compression/compression_scankey.c @@ -301,7 +301,6 @@ build_index_scankeys(Relation index_rel, List *index_filters, int *num_scankeys) filter->collation, filter->opcode, filter->value ? filter->value->constvalue : 0); - break; } } } diff --git a/tsl/test/shared/expected/compression_dml.out b/tsl/test/shared/expected/compression_dml.out index 7faf6294c3f..04203c437ba 100644 --- a/tsl/test/shared/expected/compression_dml.out +++ b/tsl/test/shared/expected/compression_dml.out @@ -933,3 +933,78 @@ ROLLBACK; DEALLOCATE prep; DROP TABLE t8241 cascade; RESET plan_cache_mode; +-- test multiple constraints on same column +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where device_id IS NOT NULL AND device_id = 1;ROLLBACK; +--- QUERY PLAN --- + Custom Scan (ModifyHypertable) (actual rows=0.00 loops=1) + Batches decompressed: 14 + Tuples decompressed: 12076 + -> Delete on metrics_compressed (actual rows=0.00 loops=1) + Delete on _hyper_X_X_chunk metrics_compressed_1 + Delete on _hyper_X_X_chunk metrics_compressed_2 + Delete on _hyper_X_X_chunk metrics_compressed_3 + -> Append (actual rows=13674.00 loops=1) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_1 (actual rows=3598.00 loops=1) + Filter: ((device_id IS NOT NULL) AND (device_id = 1)) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_2 (actual rows=5038.00 loops=1) + Filter: ((device_id IS NOT NULL) AND (device_id = 1)) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_3 (actual rows=5038.00 loops=1) + Filter: ((device_id IS NOT NULL) AND (device_id = 1)) + +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where device_id <> 1 AND device_id <> 2;ROLLBACK; +--- QUERY PLAN --- + Custom Scan (ModifyHypertable) (actual rows=0.00 loops=1) + Batches decompressed: 48 + Tuples decompressed: 41022 + -> Delete on metrics_compressed (actual rows=0.00 loops=1) + Delete on _hyper_X_X_chunk metrics_compressed_1 + Delete on _hyper_X_X_chunk metrics_compressed_2 + Delete on _hyper_X_X_chunk metrics_compressed_3 + -> Append (actual rows=41022.00 loops=1) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_1 (actual rows=10794.00 loops=1) + Filter: ((device_id <> 1) AND (device_id <> 2)) + Rows Removed by Filter: 1598 + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_2 (actual rows=15114.00 loops=1) + Filter: ((device_id <> 1) AND (device_id <> 2)) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_3 (actual rows=15114.00 loops=1) + Filter: ((device_id <> 1) AND (device_id <> 2)) + +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where device_id > 1 AND device_id < 3;ROLLBACK; +--- QUERY PLAN --- + Custom Scan (ModifyHypertable) (actual rows=0.00 loops=1) + Batches decompressed: 16 + Tuples decompressed: 13674 + -> Delete on metrics_compressed (actual rows=0.00 loops=1) + Delete on _hyper_X_X_chunk metrics_compressed_1 + Delete on _hyper_X_X_chunk metrics_compressed_2 + Delete on _hyper_X_X_chunk metrics_compressed_3 + -> Append (actual rows=13674.00 loops=1) + -> Index Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk metrics_compressed_1 (actual rows=3598.00 loops=1) + Index Cond: ((device_id > 1) AND (device_id < 3)) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_2 (actual rows=5038.00 loops=1) + Filter: ((device_id > 1) AND (device_id < 3)) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_3 (actual rows=5038.00 loops=1) + Filter: ((device_id > 1) AND (device_id < 3)) + +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where v3 IS NOT NULL AND device_id IS NOT NULL AND device_id = 1;ROLLBACK; +--- QUERY PLAN --- + Custom Scan (ModifyHypertable) (actual rows=0.00 loops=1) + Batches decompressed: 14 + Tuples decompressed: 12076 + -> Delete on metrics_compressed (actual rows=0.00 loops=1) + Delete on _hyper_X_X_chunk metrics_compressed_1 + Delete on _hyper_X_X_chunk metrics_compressed_2 + Delete on _hyper_X_X_chunk metrics_compressed_3 + -> Append (actual rows=719.00 loops=1) + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_1 (actual rows=719.00 loops=1) + Filter: ((v3 IS NOT NULL) AND (device_id IS NOT NULL) AND (device_id = 1)) + Rows Removed by Filter: 2879 + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_2 (actual rows=0.00 loops=1) + Filter: ((v3 IS NOT NULL) AND (device_id IS NOT NULL) AND (device_id = 1)) + Rows Removed by Filter: 5038 + -> Seq Scan on _hyper_X_X_chunk metrics_compressed_3 (actual rows=0.00 loops=1) + Filter: ((v3 IS NOT NULL) AND (device_id IS NOT NULL) AND (device_id = 1)) + Rows Removed by Filter: 5038 + +-- clean up dml artefacts to prevent plan switches on subsequent tests +VACUUM FULL ANALYZE metrics_compressed; diff --git a/tsl/test/shared/expected/transparent_decompress_chunk-15.out b/tsl/test/shared/expected/transparent_decompress_chunk-15.out index 7856971207a..cf1498f979a 100644 --- a/tsl/test/shared/expected/transparent_decompress_chunk-15.out +++ b/tsl/test/shared/expected/transparent_decompress_chunk-15.out @@ -112,8 +112,9 @@ ORDER BY time, device_id; Append (actual rows=0.00 loops=1) -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id < 0) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id < 0) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id < 0) + Rows Removed by Filter: 18 -> Index Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk (actual rows=0.00 loops=1) Index Cond: (device_id < 0) @@ -232,8 +233,9 @@ ORDER BY time, device_id; -> Append (actual rows=0.00 loops=1) -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id IS NULL) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id IS NULL) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id IS NULL) + Rows Removed by Filter: 18 -> Index Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk (actual rows=0.00 loops=1) Index Cond: (device_id IS NULL) @@ -694,9 +696,9 @@ SET enable_seqscan TO FALSE; -> Index Scan Backward using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on _timescaledb_internal.compress_hyper_X_X_chunk (actual rows=2.00 loops=1) Output: compress_hyper_X_X_chunk."time", compress_hyper_X_X_chunk._ts_meta_min_1, compress_hyper_X_X_chunk._ts_meta_max_1, compress_hyper_X_X_chunk.device_id, compress_hyper_X_X_chunk._ts_meta_count Index Cond: (compress_hyper_X_X_chunk.device_id = 1) - -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) + -> Index Scan using _hyper_X_X_chunk_metrics_compressed_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) Output: _hyper_X_X_chunk."time", _hyper_X_X_chunk.device_id - Index Cond: (_hyper_X_X_chunk.device_id = 1) + Filter: (_hyper_X_X_chunk.device_id = 1) -- globs should not plan IndexOnlyScans :PREFIX_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id = 1 ORDER BY device_id, time; @@ -1229,8 +1231,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.00 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 50 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.00 loops=29) Index Cond: ((device_id = $1) AND ("time" = g."time")) @@ -1243,8 +1246,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.00 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 81 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.00 loops=27) Index Cond: ((device_id = $1) AND ("time" = g."time")) diff --git a/tsl/test/shared/expected/transparent_decompress_chunk-16.out b/tsl/test/shared/expected/transparent_decompress_chunk-16.out index 80d686052c9..835112fbcc2 100644 --- a/tsl/test/shared/expected/transparent_decompress_chunk-16.out +++ b/tsl/test/shared/expected/transparent_decompress_chunk-16.out @@ -112,8 +112,9 @@ ORDER BY time, device_id; Append (actual rows=0.00 loops=1) -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id < 0) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id < 0) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id < 0) + Rows Removed by Filter: 18 -> Index Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk (actual rows=0.00 loops=1) Index Cond: (device_id < 0) @@ -232,8 +233,9 @@ ORDER BY time, device_id; -> Append (actual rows=0.00 loops=1) -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id IS NULL) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id IS NULL) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id IS NULL) + Rows Removed by Filter: 18 -> Index Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk (actual rows=0.00 loops=1) Index Cond: (device_id IS NULL) @@ -694,9 +696,9 @@ SET enable_seqscan TO FALSE; -> Index Scan Backward using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on _timescaledb_internal.compress_hyper_X_X_chunk (actual rows=2.00 loops=1) Output: compress_hyper_X_X_chunk."time", compress_hyper_X_X_chunk._ts_meta_min_1, compress_hyper_X_X_chunk._ts_meta_max_1, compress_hyper_X_X_chunk.device_id, compress_hyper_X_X_chunk._ts_meta_count Index Cond: (compress_hyper_X_X_chunk.device_id = 1) - -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) + -> Index Scan using _hyper_X_X_chunk_metrics_compressed_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) Output: _hyper_X_X_chunk."time", _hyper_X_X_chunk.device_id - Index Cond: (_hyper_X_X_chunk.device_id = 1) + Filter: (_hyper_X_X_chunk.device_id = 1) -- globs should not plan IndexOnlyScans :PREFIX_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id = 1 ORDER BY device_id, time; @@ -1229,8 +1231,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.00 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 50 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.00 loops=29) Index Cond: ((device_id = $1) AND ("time" = g."time")) @@ -1243,8 +1246,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.00 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 81 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.00 loops=27) Index Cond: ((device_id = $1) AND ("time" = g."time")) diff --git a/tsl/test/shared/expected/transparent_decompress_chunk-17.out b/tsl/test/shared/expected/transparent_decompress_chunk-17.out index ef64ee1ff54..f2d11a036e6 100644 --- a/tsl/test/shared/expected/transparent_decompress_chunk-17.out +++ b/tsl/test/shared/expected/transparent_decompress_chunk-17.out @@ -112,8 +112,9 @@ ORDER BY time, device_id; Append (actual rows=0.00 loops=1) -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id < 0) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id < 0) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id < 0) + Rows Removed by Filter: 18 -> Index Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk (actual rows=0.00 loops=1) Index Cond: (device_id < 0) @@ -244,8 +245,9 @@ ORDER BY time, device_id; Sort Method: quicksort -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id IS NULL) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id IS NULL) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id IS NULL) + Rows Removed by Filter: 18 -> Sort (actual rows=0.00 loops=1) Sort Key: _hyper_X_X_chunk."time", _hyper_X_X_chunk.device_id Sort Method: quicksort @@ -760,9 +762,9 @@ SET enable_seqscan TO FALSE; -> Index Scan Backward using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on _timescaledb_internal.compress_hyper_X_X_chunk (actual rows=2.00 loops=1) Output: compress_hyper_X_X_chunk."time", compress_hyper_X_X_chunk._ts_meta_min_1, compress_hyper_X_X_chunk._ts_meta_max_1, compress_hyper_X_X_chunk.device_id, compress_hyper_X_X_chunk._ts_meta_count Index Cond: (compress_hyper_X_X_chunk.device_id = 1) - -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) + -> Index Scan using _hyper_X_X_chunk_metrics_compressed_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) Output: _hyper_X_X_chunk."time", _hyper_X_X_chunk.device_id - Index Cond: (_hyper_X_X_chunk.device_id = 1) + Filter: (_hyper_X_X_chunk.device_id = 1) -- globs should not plan IndexOnlyScans :PREFIX_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id = 1 ORDER BY device_id, time; @@ -1295,8 +1297,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.00 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 50 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.00 loops=29) Index Cond: ((device_id = $1) AND ("time" = g."time")) @@ -1309,8 +1312,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.00 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 81 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.00 loops=27) Index Cond: ((device_id = $1) AND ("time" = g."time")) diff --git a/tsl/test/shared/expected/transparent_decompress_chunk-18.out b/tsl/test/shared/expected/transparent_decompress_chunk-18.out index aca9761431b..c0a0896a60a 100644 --- a/tsl/test/shared/expected/transparent_decompress_chunk-18.out +++ b/tsl/test/shared/expected/transparent_decompress_chunk-18.out @@ -112,8 +112,9 @@ ORDER BY time, device_id; Append (actual rows=0.00 loops=1) -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id < 0) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id < 0) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id < 0) + Rows Removed by Filter: 18 -> Index Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk (actual rows=0.00 loops=1) Index Cond: (device_id < 0) @@ -244,8 +245,9 @@ ORDER BY time, device_id; Sort Method: quicksort -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk (actual rows=0.00 loops=1) Filter: (device_id IS NULL) - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) - Index Cond: (device_id IS NULL) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.00 loops=1) + Filter: (device_id IS NULL) + Rows Removed by Filter: 18 -> Sort (actual rows=0.00 loops=1) Sort Key: _hyper_X_X_chunk."time", _hyper_X_X_chunk.device_id Sort Method: quicksort @@ -760,9 +762,9 @@ SET enable_seqscan TO FALSE; -> Index Scan Backward using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on _timescaledb_internal.compress_hyper_X_X_chunk (actual rows=2.00 loops=1) Output: compress_hyper_X_X_chunk."time", compress_hyper_X_X_chunk._ts_meta_min_1, compress_hyper_X_X_chunk._ts_meta_max_1, compress_hyper_X_X_chunk.device_id, compress_hyper_X_X_chunk._ts_meta_count Index Cond: (compress_hyper_X_X_chunk.device_id = 1) - -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) + -> Index Scan using _hyper_X_X_chunk_metrics_compressed_time_idx on _timescaledb_internal._hyper_X_X_chunk (actual rows=1598.00 loops=1) Output: _hyper_X_X_chunk."time", _hyper_X_X_chunk.device_id - Index Cond: (_hyper_X_X_chunk.device_id = 1) + Filter: (_hyper_X_X_chunk.device_id = 1) -- globs should not plan IndexOnlyScans :PREFIX_VERBOSE SELECT * FROM :TEST_TABLE WHERE device_id = 1 ORDER BY device_id, time; @@ -1275,8 +1277,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.09 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 50 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.09 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.09 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.07 loops=29) Index Cond: ((device_id = $1) AND ("time" = g."time")) @@ -1289,8 +1292,9 @@ FROM generate_series('2000-01-01'::timestamptz, '2000-02-01'::timestamptz, '1d': -> Custom Scan (ColumnarScan) on _hyper_X_X_chunk m1 (actual rows=0.16 loops=32) Filter: (("time" = g."time") AND (device_id = $1)) Rows Removed by Filter: 81 - -> Index Scan using compress_hyper_X_X_chunk_device_id__ts_meta_min_1__ts_meta_idx on compress_hyper_X_X_chunk (actual rows=0.16 loops=32) - Index Cond: ((device_id = $1) AND (_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time")) + -> Seq Scan on compress_hyper_X_X_chunk (actual rows=0.16 loops=32) + Filter: ((_ts_meta_min_1 <= g."time") AND (_ts_meta_max_1 >= g."time") AND (device_id = $1)) + Rows Removed by Filter: 16 -> Index Only Scan using _hyper_X_X_chunk_metrics_compressed_device_id_time_idx on _hyper_X_X_chunk m1 (actual rows=0.00 loops=27) Index Cond: ((device_id = $1) AND ("time" = g."time")) diff --git a/tsl/test/shared/sql/compression_dml.sql b/tsl/test/shared/sql/compression_dml.sql index 8d5542ce4c7..7710de5adf4 100644 --- a/tsl/test/shared/sql/compression_dml.sql +++ b/tsl/test/shared/sql/compression_dml.sql @@ -394,3 +394,13 @@ ROLLBACK; DEALLOCATE prep; DROP TABLE t8241 cascade; RESET plan_cache_mode; + +-- test multiple constraints on same column +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where device_id IS NOT NULL AND device_id = 1;ROLLBACK; +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where device_id <> 1 AND device_id <> 2;ROLLBACK; +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where device_id > 1 AND device_id < 3;ROLLBACK; +BEGIN; EXPLAIN (analyze, buffers off, costs off, timing off, summary off) DELETE FROM metrics_compressed where v3 IS NOT NULL AND device_id IS NOT NULL AND device_id = 1;ROLLBACK; + +-- clean up dml artefacts to prevent plan switches on subsequent tests +VACUUM FULL ANALYZE metrics_compressed; +