@@ -137,7 +137,7 @@ static double preprocess_limit(PlannerInfo *root,
137
137
double tuple_fraction ,
138
138
int64 * offset_est , int64 * count_est );
139
139
static void remove_useless_groupby_columns (PlannerInfo * root );
140
- static List * groupclause_apply_groupingset (PlannerInfo * root , List * force );
140
+ static List * preprocess_groupclause (PlannerInfo * root , List * force );
141
141
static List * extract_rollup_sets (List * groupingSets );
142
142
static List * reorder_grouping_sets (List * groupingSets , List * sortclause );
143
143
static void standard_qp_callback (PlannerInfo * root , void * extra );
@@ -1422,7 +1422,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
1422
1422
else if (parse -> groupClause )
1423
1423
{
1424
1424
/* Preprocess regular GROUP BY clause, if any */
1425
- root -> processed_groupClause = list_copy ( parse -> groupClause );
1425
+ root -> processed_groupClause = preprocess_groupclause ( root , NIL );
1426
1426
/* Remove any redundant GROUP BY columns */
1427
1427
remove_useless_groupby_columns (root );
1428
1428
}
@@ -2169,7 +2169,7 @@ preprocess_grouping_sets(PlannerInfo *root)
2169
2169
* The groupClauses for hashed grouping sets are built later on.)
2170
2170
*/
2171
2171
if (gs -> set )
2172
- rollup -> groupClause = groupclause_apply_groupingset (root , gs -> set );
2172
+ rollup -> groupClause = preprocess_groupclause (root , gs -> set );
2173
2173
else
2174
2174
rollup -> groupClause = NIL ;
2175
2175
@@ -2821,24 +2821,106 @@ remove_useless_groupby_columns(PlannerInfo *root)
2821
2821
}
2822
2822
2823
2823
/*
2824
- * groupclause_apply_groupingset
2825
- * Apply the order of GROUP BY clauses defined by grouping sets. Items
2826
- * not in the grouping set are skipped.
2824
+ * preprocess_groupclause - do preparatory work on GROUP BY clause
2825
+ *
2826
+ * The idea here is to adjust the ordering of the GROUP BY elements
2827
+ * (which in itself is semantically insignificant) to match ORDER BY,
2828
+ * thereby allowing a single sort operation to both implement the ORDER BY
2829
+ * requirement and set up for a Unique step that implements GROUP BY.
2830
+ * We also consider partial match between GROUP BY and ORDER BY elements,
2831
+ * which could allow to implement ORDER BY using the incremental sort.
2832
+ *
2833
+ * We also consider other orderings of the GROUP BY elements, which could
2834
+ * match the sort ordering of other possible plans (eg an indexscan) and
2835
+ * thereby reduce cost. This is implemented during the generation of grouping
2836
+ * paths. See get_useful_group_keys_orderings() for details.
2837
+ *
2838
+ * Note: we need no comparable processing of the distinctClause because
2839
+ * the parser already enforced that that matches ORDER BY.
2840
+ *
2841
+ * Note: we return a fresh List, but its elements are the same
2842
+ * SortGroupClauses appearing in parse->groupClause. This is important
2843
+ * because later processing may modify the processed_groupClause list.
2844
+ *
2845
+ * For grouping sets, the order of items is instead forced to agree with that
2846
+ * of the grouping set (and items not in the grouping set are skipped). The
2847
+ * work of sorting the order of grouping set elements to match the ORDER BY if
2848
+ * possible is done elsewhere.
2827
2849
*/
2828
2850
static List *
2829
- groupclause_apply_groupingset (PlannerInfo * root , List * gset )
2851
+ preprocess_groupclause (PlannerInfo * root , List * force )
2830
2852
{
2831
2853
Query * parse = root -> parse ;
2832
2854
List * new_groupclause = NIL ;
2833
2855
ListCell * sl ;
2856
+ ListCell * gl ;
2834
2857
2835
- foreach (sl , gset )
2858
+ /* For grouping sets, we need to force the ordering */
2859
+ if (force )
2836
2860
{
2837
- Index ref = lfirst_int (sl );
2838
- SortGroupClause * cl = get_sortgroupref_clause (ref , parse -> groupClause );
2861
+ foreach (sl , force )
2862
+ {
2863
+ Index ref = lfirst_int (sl );
2864
+ SortGroupClause * cl = get_sortgroupref_clause (ref , parse -> groupClause );
2865
+
2866
+ new_groupclause = lappend (new_groupclause , cl );
2867
+ }
2839
2868
2840
- new_groupclause = lappend ( new_groupclause , cl ) ;
2869
+ return new_groupclause ;
2841
2870
}
2871
+
2872
+ /* If no ORDER BY, nothing useful to do here */
2873
+ if (parse -> sortClause == NIL )
2874
+ return list_copy (parse -> groupClause );
2875
+
2876
+ /*
2877
+ * Scan the ORDER BY clause and construct a list of matching GROUP BY
2878
+ * items, but only as far as we can make a matching prefix.
2879
+ *
2880
+ * This code assumes that the sortClause contains no duplicate items.
2881
+ */
2882
+ foreach (sl , parse -> sortClause )
2883
+ {
2884
+ SortGroupClause * sc = lfirst_node (SortGroupClause , sl );
2885
+
2886
+ foreach (gl , parse -> groupClause )
2887
+ {
2888
+ SortGroupClause * gc = lfirst_node (SortGroupClause , gl );
2889
+
2890
+ if (equal (gc , sc ))
2891
+ {
2892
+ new_groupclause = lappend (new_groupclause , gc );
2893
+ break ;
2894
+ }
2895
+ }
2896
+ if (gl == NULL )
2897
+ break ; /* no match, so stop scanning */
2898
+ }
2899
+
2900
+
2901
+ /* If no match at all, no point in reordering GROUP BY */
2902
+ if (new_groupclause == NIL )
2903
+ return list_copy (parse -> groupClause );
2904
+
2905
+ /*
2906
+ * Add any remaining GROUP BY items to the new list. We don't require a
2907
+ * complete match, because even partial match allows ORDER BY to be
2908
+ * implemented using incremental sort. Also, give up if there are any
2909
+ * non-sortable GROUP BY items, since then there's no hope anyway.
2910
+ */
2911
+ foreach (gl , parse -> groupClause )
2912
+ {
2913
+ SortGroupClause * gc = lfirst_node (SortGroupClause , gl );
2914
+
2915
+ if (list_member_ptr (new_groupclause , gc ))
2916
+ continue ; /* it matched an ORDER BY item */
2917
+ if (!OidIsValid (gc -> sortop )) /* give up, GROUP BY can't be sorted */
2918
+ return list_copy (parse -> groupClause );
2919
+ new_groupclause = lappend (new_groupclause , gc );
2920
+ }
2921
+
2922
+ /* Success --- install the rearranged GROUP BY list */
2923
+ Assert (list_length (parse -> groupClause ) == list_length (new_groupclause ));
2842
2924
return new_groupclause ;
2843
2925
}
2844
2926
@@ -4170,7 +4252,7 @@ consider_groupingsets_paths(PlannerInfo *root,
4170
4252
{
4171
4253
rollup = makeNode (RollupData );
4172
4254
4173
- rollup -> groupClause = groupclause_apply_groupingset (root , gset );
4255
+ rollup -> groupClause = preprocess_groupclause (root , gset );
4174
4256
rollup -> gsets_data = list_make1 (gs );
4175
4257
rollup -> gsets = remap_to_groupclause_idx (rollup -> groupClause ,
4176
4258
rollup -> gsets_data ,
@@ -4359,7 +4441,7 @@ consider_groupingsets_paths(PlannerInfo *root,
4359
4441
4360
4442
Assert (gs -> set != NIL );
4361
4443
4362
- rollup -> groupClause = groupclause_apply_groupingset (root , gs -> set );
4444
+ rollup -> groupClause = preprocess_groupclause (root , gs -> set );
4363
4445
rollup -> gsets_data = list_make1 (gs );
4364
4446
rollup -> gsets = remap_to_groupclause_idx (rollup -> groupClause ,
4365
4447
rollup -> gsets_data ,
0 commit comments