@@ -978,27 +978,45 @@ cost_batch_sorted_merge(PlannerInfo *root, const CompressionInfo *compression_in
978978{
979979 Path sort_path ; /* dummy for result of cost_sort */
980980
981- /*
982- * Don't disable the compressed batch sorted merge plan with the enable_sort
983- * GUC. We have a separate GUC for it, and this way you can try to force the
984- * batch sorted merge plan by disabling sort.
985- */
986- const bool old_enable_sort = enable_sort ;
987- enable_sort = true;
988- cost_sort (& sort_path ,
989- root ,
990- dcpath -> required_compressed_pathkeys ,
981+ /* We are utilizing compressed sort for batch sorted merge: do not need extra sort */
982+ if (dcpath -> required_compressed_pathkeys &&
983+ pathkeys_contained_in (dcpath -> required_compressed_pathkeys , compressed_path -> pathkeys ))
984+ {
985+ sort_path .rows = compressed_path -> rows ;
986+ sort_path .startup_cost = compressed_path -> startup_cost ;
987+ sort_path .total_cost = compressed_path -> total_cost ;
991988#if PG18_GE
992- compressed_path -> disabled_nodes ,
989+ /* PG18 changes the way we handle disabled nodes so we
990+ * need to take those into account as well.
991+ *
992+ * https://github.com/postgres/postgres/commit/e2225346
993+ */
994+ sort_path .disabled_nodes = compressed_path -> disabled_nodes ;
993995#endif
994- compressed_path -> total_cost ,
995- compressed_path -> rows ,
996- compressed_path -> pathtarget -> width ,
997- 0.0 ,
998- work_mem ,
999- -1 );
1000- enable_sort = old_enable_sort ;
1001-
996+ }
997+ else
998+ {
999+ /*
1000+ * Don't disable the compressed batch sorted merge plan with the enable_sort
1001+ * GUC. We have a separate GUC for it, and this way you can try to force the
1002+ * batch sorted merge plan by disabling sort.
1003+ */
1004+ const bool old_enable_sort = enable_sort ;
1005+ enable_sort = true;
1006+ cost_sort (& sort_path ,
1007+ root ,
1008+ dcpath -> required_compressed_pathkeys ,
1009+ #if PG18_GE
1010+ compressed_path -> disabled_nodes ,
1011+ #endif
1012+ compressed_path -> total_cost ,
1013+ compressed_path -> rows ,
1014+ compressed_path -> pathtarget -> width ,
1015+ 0.0 ,
1016+ work_mem ,
1017+ -1 );
1018+ enable_sort = old_enable_sort ;
1019+ }
10021020 /*
10031021 * In compressed batch sorted merge, for each distinct segmentby value we
10041022 * have to keep the corresponding latest batch open. Estimate the number of
@@ -1432,6 +1450,18 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
14321450 return NIL ;
14331451 }
14341452
1453+ if (ts_guc_debug_require_batch_sorted_merge == DRO_Require ||
1454+ ts_guc_debug_require_batch_sorted_merge == DRO_Force )
1455+ {
1456+ if (!sort_info -> use_batch_sorted_merge && ts_guc_enable_decompression_sorted_merge )
1457+ {
1458+ ereport (ERROR ,
1459+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
1460+ errmsg ("debug: batch sorted merge is required but not possible at planning "
1461+ "time" )));
1462+ }
1463+ }
1464+
14351465 /*
14361466 * Filter out all paths that try to JOIN the compressed chunk on the
14371467 * hypertable or the uncompressed chunk
@@ -1477,8 +1507,6 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
14771507 */
14781508 if (sort_info -> use_batch_sorted_merge && ts_guc_enable_decompression_sorted_merge )
14791509 {
1480- Assert (!sort_info -> use_compressed_sort );
1481-
14821510 ColumnarScanPath * path_copy =
14831511 copy_columnar_scan_path ((ColumnarScanPath * ) chunk_path_no_sort );
14841512
@@ -1492,6 +1520,12 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
14921520 * query here.
14931521 */
14941522 path_copy -> custom_path .path .pathkeys = sort_info -> decompressed_sort_pathkeys ;
1523+
1524+ /* Batch sorted merge over unordered chunk can utilize compressed sort, copy the relevant
1525+ * fields */
1526+ path_copy -> needs_sequence_num = sort_info -> needs_sequence_num ;
1527+ path_copy -> required_compressed_pathkeys = sort_info -> required_compressed_pathkeys ;
1528+
14951529 cost_batch_sorted_merge (root , compression_info , path_copy , compressed_path );
14961530
14971531 if (ts_guc_debug_require_batch_sorted_merge == DRO_Force )
@@ -1502,22 +1536,13 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
15021536
15031537 decompressed_paths = lappend (decompressed_paths , path_copy );
15041538 }
1505- else if (ts_guc_debug_require_batch_sorted_merge == DRO_Require ||
1506- ts_guc_debug_require_batch_sorted_merge == DRO_Force )
1507- {
1508- ereport (ERROR ,
1509- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
1510- errmsg ("debug: batch sorted merge is required but not possible at planning "
1511- "time" )));
1512- }
1513-
15141539 /*
15151540 * If we can push down the sort below the ColumnarScan node, we set the
15161541 * pathkeys of the decompress node to the decompressed_sort_pathkeys. We
15171542 * will determine whether to put an actual sort between the decompression
15181543 * node and the scan during plan creation.
15191544 */
1520- if (sort_info -> use_compressed_sort )
1545+ else if (sort_info -> use_compressed_sort )
15211546 {
15221547 ColumnarScanPath * columnar_scan_with_compressed_sort = NULL ;
15231548 Path dummy_sort_path ; /* dummy for result of cost_sort */
@@ -1600,15 +1625,14 @@ build_on_single_compressed_path(PlannerInfo *root, const Chunk *chunk, RelOptInf
16001625 decompressed_paths = lappend (decompressed_paths , path_copy );
16011626 }
16021627 }
1603-
16041628 /*
16051629 * Also try explicit sort after decompression, if we couldn't push down the
1606- * sort. Don't do this for parallel plans, because in this case it is
1630+ * sort or do batch sorted merge . Don't do this for parallel plans, because in this case it is
16071631 * typically done with Sort under Gather node. This splits the Sort in
16081632 * per-worker buckets, so splitting the buckets further per-chunk is less
16091633 * important.
16101634 */
1611- if (! sort_info -> use_compressed_sort && chunk_path_no_sort -> parallel_workers == 0 )
1635+ else if (chunk_path_no_sort -> parallel_workers == 0 )
16121636 {
16131637 Path * sort_above_chunk =
16141638 make_chunk_sorted_path (root , chunk_rel , chunk_path_no_sort , compressed_path , sort_info );
@@ -3229,6 +3253,18 @@ build_sortinfo(PlannerInfo *root, const Chunk *chunk, RelOptInfo *chunk_rel,
32293253 compression_info ,
32303254 /* for_batch_sorted_merge = */ true,
32313255 & sort_info .reverse );
3256+
3257+ /* Pathkeys are matching leading orderby metadata column:
3258+ * can use already sorted compressed data for batch sorted merge.
3259+ */
3260+ if (sort_info .use_batch_sorted_merge && ts_guc_enable_decompression_sorted_merge &&
3261+ !sort_info .reverse && list_length (pathkeys ) == 1 &&
3262+ /* Needs to be sorted on metadata, not sequence number */
3263+ !compression_info -> has_seq_num )
3264+ {
3265+ sort_info .needs_sequence_num = true;
3266+ sort_info .use_compressed_sort = true;
3267+ }
32323268 }
32333269 return sort_info ;
32343270 }
0 commit comments