Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b30f9f0
Try row-by-row decompression if we consider startup cost
akuzm May 7, 2026
ee5212b
fixes
akuzm May 7, 2026
95aa369
accept some test changes
akuzm May 7, 2026
ce597a4
Merge remote-tracking branch 'origin/main' into HEAD
akuzm May 13, 2026
0ee0d91
Merge remote-tracking branch 'origin/main' into HEAD
akuzm May 13, 2026
5c3c472
accept some test changes
akuzm May 13, 2026
ff94191
accept row-by-row decompression under skip scan
akuzm May 13, 2026
26fb80e
Merge remote-tracking branch 'origin/main' into HEAD
akuzm May 19, 2026
dfaae5d
accept some refs
akuzm May 19, 2026
6b93109
reference REL_15_13 plan_skip_scan plan_skip_scan_dagg transparent_de…
akuzm May 19, 2026
796136a
reference REL_18_3 plan_skip_scan plan_skip_scan_dagg transparent_dec…
akuzm May 19, 2026
d9cf521
reference REL_16_8 plan_skip_scan plan_skip_scan_dagg transparent_dec…
akuzm May 19, 2026
e5d4e7f
comment
akuzm May 20, 2026
5f91d90
increase limit for stable plans on pg 18
akuzm May 20, 2026
b1345eb
update ref on 18
akuzm May 20, 2026
13c8dec
Merge commit '0e49a7a5463afd84a126dd0b10b7d15269214cc4~' into HEAD
akuzm Jun 2, 2026
8a5648d
Merge commit '0e49a7a5463afd84a126dd0b10b7d15269214cc4' into HEAD
akuzm Jun 2, 2026
ad621e0
Merge commit '14583bd~' into HEAD
akuzm Jun 2, 2026
1e2f2e7
Merge commit '14583bd' into HEAD
akuzm Jun 2, 2026
a0de631
Merge remote-tracking branch 'origin/main' into HEAD
akuzm Jun 2, 2026
f69f4f4
ref
akuzm Jun 2, 2026
1c2ad3e
reference REL_15_13 plan_skip_scan plan_skip_scan_dagg transparent_de…
akuzm Jun 4, 2026
daf1526
reference REL_18_3 plan_skip_scan plan_skip_scan_dagg transparent_dec…
akuzm Jun 4, 2026
bb32bda
reference REL_16_8 plan_skip_scan plan_skip_scan_dagg transparent_dec…
akuzm Jun 4, 2026
6759124
reference REL_18_3 columnar_index_scan plan_skip_scan plan_skip_scan_…
akuzm Jun 4, 2026
8a410aa
changelog
akuzm Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .unreleased/row-by-row
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implements: #9732 Speed up some queries with small LIMIT by switching to row-by-row query execution pipeline
34 changes: 31 additions & 3 deletions tsl/src/nodes/columnar_scan/columnar_scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,10 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
*/
if (sort_info->use_compressed_sort)
{
ColumnarScanPath *columnar_scan_with_compressed_sort = NULL;
Path dummy_sort_path; /* dummy for result of cost_sort */
Path *compressed_path_for_cost = NULL;

if (pathkeys_contained_in(sort_info->required_compressed_pathkeys,
compressed_path->pathkeys))
{
Expand All @@ -1502,6 +1506,9 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
path->needs_sequence_num = sort_info->needs_sequence_num;
path->required_compressed_pathkeys = sort_info->required_compressed_pathkeys;
path->custom_path.path.pathkeys = sort_info->decompressed_sort_pathkeys;

columnar_scan_with_compressed_sort = path;
compressed_path_for_cost = linitial(path->custom_path.custom_paths);
}
else
{
Expand All @@ -1523,9 +1530,8 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
* creation. Examples of this in: create_merge_append_path &
* create_merge_append_plan
*/
Path sort_path; /* dummy for result of cost_sort */

cost_sort(&sort_path,
cost_sort(&dummy_sort_path,
root,
sort_info->required_compressed_pathkeys,
#if PG18_GE
Expand All @@ -1538,7 +1544,29 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
work_mem,
-1);

cost_columnar_scan(compression_info, path_copy, &sort_path);
cost_columnar_scan(compression_info, path_copy, &dummy_sort_path);

decompressed_paths = lappend(decompressed_paths, path_copy);

columnar_scan_with_compressed_sort = path_copy;
compressed_path_for_cost = &dummy_sort_path;
}

if (chunk_rel->consider_startup &&
columnar_scan_with_compressed_sort->enable_bulk_decompression)
{
/*
* Try a version with row-by-row decompression too, if the planner
* requests the paths with cheap startup. Typically it happens with
* ORDER BY + LIMIT. Row-by-row decompression is only useful if
* there is no sort above the columnar scan, because a sort would
* require a full decompression anyway.
*/
ColumnarScanPath *path_copy =
copy_columnar_scan_path(columnar_scan_with_compressed_sort);
path_copy->enable_bulk_decompression = false;

cost_columnar_scan(compression_info, path_copy, compressed_path_for_cost);

decompressed_paths = lappend(decompressed_paths, path_copy);
}
Expand Down
20 changes: 10 additions & 10 deletions tsl/test/expected/columnar_index_scan-17.out
Original file line number Diff line number Diff line change
Expand Up @@ -1700,10 +1700,9 @@ SELECT device, max(time), min(time) FROM metrics GROUP BY device ORDER BY 1,2,3;
Aggregate
-> Merge Append
Sort Key: metrics.device
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk
-> Sort
Sort Key: compress_hyper_2_2_chunk.device
-> Seq Scan on compress_hyper_2_2_chunk
-> Custom Scan (SkipScan) on _hyper_1_1_chunk
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk
-> Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_v2_last_tim_idx on compress_hyper_2_2_chunk
-> Sort
Sort Key: _hyper_1_1_chunk.device
-> Seq Scan on _hyper_1_1_chunk
Expand Down Expand Up @@ -1822,13 +1821,14 @@ SELECT device, max(time), min(time) FROM metrics GROUP BY device ORDER BY 1,2,3;
-- DISTINCT on a segmentby column (Agg with no Aggrefs)
:PREFIX SELECT DISTINCT device FROM metrics ORDER BY device;
--- QUERY PLAN ---
Sort
Sort Key: metrics.device
-> HashAggregate
Group Key: metrics.device
-> Append
Unique
-> Merge Append
Sort Key: metrics.device
-> Custom Scan (SkipScan) on _hyper_1_1_chunk
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk
-> Seq Scan on compress_hyper_2_2_chunk
-> Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_v2_last_tim_idx on compress_hyper_2_2_chunk
-> Sort
Sort Key: _hyper_1_1_chunk.device
-> Seq Scan on _hyper_1_1_chunk

-- count(*) without GROUP BY
Expand Down
20 changes: 10 additions & 10 deletions tsl/test/expected/columnar_index_scan-18.out
Original file line number Diff line number Diff line change
Expand Up @@ -1702,10 +1702,9 @@ SELECT device, max(time), min(time) FROM metrics GROUP BY device ORDER BY 1,2,3;
Aggregate
-> Merge Append
Sort Key: metrics.device
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk
-> Sort
Sort Key: compress_hyper_2_2_chunk.device
-> Seq Scan on compress_hyper_2_2_chunk
-> Custom Scan (SkipScan) on _hyper_1_1_chunk
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk
-> Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_v2_last_tim_idx on compress_hyper_2_2_chunk
-> Sort
Sort Key: _hyper_1_1_chunk.device
-> Seq Scan on _hyper_1_1_chunk
Expand Down Expand Up @@ -1826,13 +1825,14 @@ SELECT device, max(time), min(time) FROM metrics GROUP BY device ORDER BY 1,2,3;
-- DISTINCT on a segmentby column (Agg with no Aggrefs)
:PREFIX SELECT DISTINCT device FROM metrics ORDER BY device;
--- QUERY PLAN ---
Sort
Sort Key: metrics.device
-> HashAggregate
Group Key: metrics.device
-> Append
Unique
-> Merge Append
Sort Key: metrics.device
-> Custom Scan (SkipScan) on _hyper_1_1_chunk
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk
-> Seq Scan on compress_hyper_2_2_chunk
-> Index Scan using compress_hyper_2_2_chunk_device_sensor__ts_meta_v2_last_tim_idx on compress_hyper_2_2_chunk
-> Sort
Sort Key: _hyper_1_1_chunk.device
-> Seq Scan on _hyper_1_1_chunk

-- count(*) without GROUP BY
Expand Down
21 changes: 12 additions & 9 deletions tsl/test/expected/compress_sort_transform.out
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ group by 1, 2 order by 1, 2 limit 1;
Group Key: ht_metrics_partially_compressed.device, (time_bucket('@ 1 min'::interval, ht_metrics_partially_compressed."time"))
-> Merge Append (actual rows=3.00 loops=1)
Sort Key: ht_metrics_partially_compressed.device, (time_bucket('@ 1 min'::interval, ht_metrics_partially_compressed."time"))
-> Custom Scan (VectorAgg) (actual rows=2.00 loops=1)
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk (actual rows=1440.00 loops=1)
-> Sort (actual rows=3.00 loops=1)
-> Partial GroupAggregate (actual rows=2.00 loops=1)
Group Key: _hyper_1_1_chunk.device, time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time")
-> Custom Scan (ColumnarScan) on _hyper_1_1_chunk (actual rows=3.00 loops=1)
-> Sort (actual rows=1.00 loops=1)
Sort Key: compress_hyper_2_4_chunk.device, compress_hyper_2_4_chunk._ts_meta_v2_first_time, compress_hyper_2_4_chunk._ts_meta_v2_last_time
Sort Method: quicksort
-> Seq Scan on compress_hyper_2_4_chunk (actual rows=3.00 loops=1)
Expand All @@ -56,9 +57,10 @@ group by 1, 2 order by 1, 2 limit 1;
Sort Key: _hyper_1_1_chunk.device, (time_bucket('@ 1 min'::interval, _hyper_1_1_chunk."time"))
Sort Method: quicksort
-> Seq Scan on _hyper_1_1_chunk (actual rows=960.00 loops=1)
-> Custom Scan (VectorAgg) (actual rows=1.00 loops=1)
-> Custom Scan (ColumnarScan) on _hyper_1_2_chunk (actual rows=1512.00 loops=1)
-> Sort (actual rows=3.00 loops=1)
-> Partial GroupAggregate (actual rows=1.00 loops=1)
Group Key: _hyper_1_2_chunk.device, time_bucket('@ 1 min'::interval, _hyper_1_2_chunk."time")
-> Custom Scan (ColumnarScan) on _hyper_1_2_chunk (actual rows=2.00 loops=1)
-> Sort (actual rows=1.00 loops=1)
Sort Key: compress_hyper_2_5_chunk.device, compress_hyper_2_5_chunk._ts_meta_v2_first_time, compress_hyper_2_5_chunk._ts_meta_v2_last_time
Sort Method: quicksort
-> Seq Scan on compress_hyper_2_5_chunk (actual rows=3.00 loops=1)
Expand All @@ -68,9 +70,10 @@ group by 1, 2 order by 1, 2 limit 1;
Sort Key: _hyper_1_2_chunk.device, (time_bucket('@ 1 min'::interval, _hyper_1_2_chunk."time"))
Sort Method: quicksort
-> Seq Scan on _hyper_1_2_chunk (actual rows=1008.00 loops=1)
-> Custom Scan (VectorAgg) (actual rows=1.00 loops=1)
-> Custom Scan (ColumnarScan) on _hyper_1_3_chunk (actual rows=507.00 loops=1)
-> Sort (actual rows=3.00 loops=1)
-> Partial GroupAggregate (actual rows=1.00 loops=1)
Group Key: _hyper_1_3_chunk.device, time_bucket('@ 1 min'::interval, _hyper_1_3_chunk."time")
-> Custom Scan (ColumnarScan) on _hyper_1_3_chunk (actual rows=2.00 loops=1)
-> Sort (actual rows=1.00 loops=1)
Sort Key: compress_hyper_2_6_chunk.device, compress_hyper_2_6_chunk._ts_meta_v2_first_time, compress_hyper_2_6_chunk._ts_meta_v2_last_time
Sort Method: quicksort
-> Seq Scan on compress_hyper_2_6_chunk (actual rows=3.00 loops=1)
Expand Down
5 changes: 1 addition & 4 deletions tsl/test/expected/merge_append_partially_compressed.out
Original file line number Diff line number Diff line change
Expand Up @@ -905,10 +905,7 @@ SELECT * FROM test1 WHERE x4 % 11 != 0 ORDER BY x1, x2, x5, time DESC, x3 ASC, x
-> Custom Scan (ColumnarScan) on _hyper_3_7_chunk (actual rows=4.00 loops=1)
Filter: ((x4 % 11) <> 0)
Rows Removed by Filter: 10000
-> Sort (actual rows=13.00 loops=1)
Sort Key: compress_hyper_4_8_chunk.x1, compress_hyper_4_8_chunk.x2, compress_hyper_4_8_chunk.x5, compress_hyper_4_8_chunk._ts_meta_v2_last_time DESC, compress_hyper_4_8_chunk._ts_meta_v2_first_time DESC, compress_hyper_4_8_chunk._ts_meta_v2_first_x3, compress_hyper_4_8_chunk._ts_meta_v2_last_x3, compress_hyper_4_8_chunk._ts_meta_v2_first_x4, compress_hyper_4_8_chunk._ts_meta_v2_last_x4
Sort Method: quicksort
-> Seq Scan on compress_hyper_4_8_chunk (actual rows=13.00 loops=1)
-> Index Scan using compress_hyper_4_8_chunk_x1_x2_x5__ts_meta_v2_last_time__ts_idx on compress_hyper_4_8_chunk (actual rows=13.00 loops=1)
-> Sort (actual rows=1.00 loops=1)
Sort Key: _hyper_3_7_chunk.x1, _hyper_3_7_chunk.x2, _hyper_3_7_chunk.x5, _hyper_3_7_chunk."time" DESC, _hyper_3_7_chunk.x3, _hyper_3_7_chunk.x4
Sort Method: quicksort
Expand Down
Loading
Loading