@ -140,7 +140,7 @@ static double preprocess_limit(PlannerInfo *root,
double tuple_fraction ,
int64 * offset_est , int64 * count_est ) ;
static void remove_useless_groupby_columns ( PlannerInfo * root ) ;
static List * preprocess_groupclause ( PlannerInfo * root , List * force ) ;
static List * groupclause_apply_groupingset ( PlannerInfo * root , List * force ) ;
static List * extract_rollup_sets ( List * groupingSets ) ;
static List * reorder_grouping_sets ( List * groupingSets , List * sortclause ) ;
static void standard_qp_callback ( PlannerInfo * root , void * extra ) ;
@ -1423,7 +1423,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
else if ( parse - > groupClause )
{
/* Preprocess regular GROUP BY clause, if any */
root - > processed_groupClause = preprocess_groupclause ( root , NIL ) ;
root - > processed_groupClause = list_copy ( parse - > groupClause ) ; ;
/* Remove any redundant GROUP BY columns */
remove_useless_groupby_columns ( root ) ;
}
@ -2144,7 +2144,7 @@ preprocess_grouping_sets(PlannerInfo *root)
* The groupClauses for hashed grouping sets are built later on . )
*/
if ( gs - > set )
rollup - > groupClause = preprocess_groupclause ( root , gs - > set ) ;
rollup - > groupClause = groupclause_apply_groupingset ( root , gs - > set ) ;
else
rollup - > groupClause = NIL ;
@ -2796,111 +2796,24 @@ remove_useless_groupby_columns(PlannerInfo *root)
}
/*
* preprocess_groupclause - do preparatory work on GROUP BY clause
*
* The idea here is to adjust the ordering of the GROUP BY elements
* ( which in itself is semantically insignificant ) to match ORDER BY ,
* thereby allowing a single sort operation to both implement the ORDER BY
* requirement and set up for a Unique step that implements GROUP BY .
*
* In principle it might be interesting to consider other orderings of the
* GROUP BY elements , which could match the sort ordering of other
* possible plans ( eg an indexscan ) and thereby reduce cost . We don ' t
* bother with that , though . Hashed grouping will frequently win anyway .
*
* Note : we need no comparable processing of the distinctClause because
* the parser already enforced that that matches ORDER BY .
*
* Note : we return a fresh List , but its elements are the same
* SortGroupClauses appearing in parse - > groupClause . This is important
* because later processing may modify the processed_groupClause list .
*
* For grouping sets , the order of items is instead forced to agree with that
* of the grouping set ( and items not in the grouping set are skipped ) . The
* work of sorting the order of grouping set elements to match the ORDER BY if
* possible is done elsewhere .
* groupclause_apply_groupingset
* Apply the order of GROUP BY clauses defined by grouping sets . Items
* not in the grouping set are skipped .
*/
static List *
preprocess_groupclause ( PlannerInfo * root , List * force )
groupclause_apply_groupingset ( PlannerInfo * root , List * gset )
{
Query * parse = root - > parse ;
List * new_groupclause = NIL ;
bool partial_match ;
ListCell * sl ;
ListCell * gl ;
/* For grouping sets, we need to force the ordering */
if ( force )
{
foreach ( sl , force )
foreach ( sl , gset )
{
Index ref = lfirst_int ( sl ) ;
SortGroupClause * cl = get_sortgroupref_clause ( ref , parse - > groupClause ) ;
new_groupclause = lappend ( new_groupclause , cl ) ;
}
return new_groupclause ;
}
/* If no ORDER BY, nothing useful to do here */
if ( parse - > sortClause = = NIL )
return list_copy ( parse - > groupClause ) ;
/*
* Scan the ORDER BY clause and construct a list of matching GROUP BY
* items , but only as far as we can make a matching prefix .
*
* This code assumes that the sortClause contains no duplicate items .
*/
foreach ( sl , parse - > sortClause )
{
SortGroupClause * sc = lfirst_node ( SortGroupClause , sl ) ;
foreach ( gl , parse - > groupClause )
{
SortGroupClause * gc = lfirst_node ( SortGroupClause , gl ) ;
if ( equal ( gc , sc ) )
{
new_groupclause = lappend ( new_groupclause , gc ) ;
break ;
}
}
if ( gl = = NULL )
break ; /* no match, so stop scanning */
}
/* Did we match all of the ORDER BY list, or just some of it? */
partial_match = ( sl ! = NULL ) ;
/* If no match at all, no point in reordering GROUP BY */
if ( new_groupclause = = NIL )
return list_copy ( parse - > groupClause ) ;
/*
* Add any remaining GROUP BY items to the new list , but only if we were
* able to make a complete match . In other words , we only rearrange the
* GROUP BY list if the result is that one list is a prefix of the other
* - - - otherwise there ' s no possibility of a common sort . Also , give up
* if there are any non - sortable GROUP BY items , since then there ' s no
* hope anyway .
*/
foreach ( gl , parse - > groupClause )
{
SortGroupClause * gc = lfirst_node ( SortGroupClause , gl ) ;
if ( list_member_ptr ( new_groupclause , gc ) )
continue ; /* it matched an ORDER BY item */
if ( partial_match ) /* give up, no common sort possible */
return list_copy ( parse - > groupClause ) ;
if ( ! OidIsValid ( gc - > sortop ) ) /* give up, GROUP BY can't be sorted */
return list_copy ( parse - > groupClause ) ;
new_groupclause = lappend ( new_groupclause , gc ) ;
}
/* Success --- install the rearranged GROUP BY list */
Assert ( list_length ( parse - > groupClause ) = = list_length ( new_groupclause ) ) ;
return new_groupclause ;
}
@ -4200,7 +4113,7 @@ consider_groupingsets_paths(PlannerInfo *root,
{
rollup = makeNode ( RollupData ) ;
rollup - > groupClause = preprocess_groupclause ( root , gset ) ;
rollup - > groupClause = groupclause_apply_groupingset ( root , gset ) ;
rollup - > gsets_data = list_make1 ( gs ) ;
rollup - > gsets = remap_to_groupclause_idx ( rollup - > groupClause ,
rollup - > gsets_data ,
@ -4389,7 +4302,7 @@ consider_groupingsets_paths(PlannerInfo *root,
Assert ( gs - > set ! = NIL ) ;
rollup - > groupClause = preprocess_groupclause ( root , gs - > set ) ;
rollup - > groupClause = groupclause_apply_groupingset ( root , gs - > set ) ;
rollup - > gsets_data = list_make1 ( gs ) ;
rollup - > gsets = remap_to_groupclause_idx ( rollup - > groupClause ,
rollup - > gsets_data ,
@ -6891,14 +6804,28 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
*/
foreach ( lc , input_rel - > pathlist )
{
ListCell * lc2 ;
Path * path = ( Path * ) lfirst ( lc ) ;
Path * path_save = path ;
List * pathkey_orderings = NIL ;
/* generate alternative group orderings that might be useful */
pathkey_orderings = get_useful_group_keys_orderings ( root , path ) ;
Assert ( list_length ( pathkey_orderings ) > 0 ) ;
foreach ( lc2 , pathkey_orderings )
{
PathKeyInfo * info = ( PathKeyInfo * ) lfirst ( lc2 ) ;
/* restore the path (we replace it in the loop) */
path = path_save ;
path = make_ordered_path ( root ,
grouped_rel ,
path ,
cheapest_path ,
root - > group_pathkeys ) ;
info - > pathkeys ) ;
if ( path = = NULL )
continue ;
@ -6912,8 +6839,8 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
else if ( parse - > hasAggs )
{
/*
* We have aggregation , possibly with plain GROUP BY . Make an
* AggPath .
* We have aggregation , possibly with plain GROUP BY . Make
* an AggPath .
*/
add_path ( grouped_rel , ( Path * )
create_agg_path ( root ,
@ -6922,7 +6849,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
grouped_rel - > reltarget ,
parse - > groupClause ? AGG_SORTED : AGG_PLAIN ,
AGGSPLIT_SIMPLE ,
root - > pro cessed_groupC lause,
info - > clauses ,
havingQual ,
agg_costs ,
dNumGroups ) ) ;
@ -6930,14 +6857,14 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
else if ( parse - > groupClause )
{
/*
* We have GROUP BY without aggregation or grouping sets . Make
* a GroupPath .
* We have GROUP BY without aggregation or grouping sets .
* Make a GroupPath .
*/
add_path ( grouped_rel , ( Path * )
create_group_path ( root ,
grouped_rel ,
path ,
root - > pro cessed_groupC lause,
info - > clauses ,
havingQual ,
dNumGroups ) ) ;
}
@ -6947,6 +6874,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
Assert ( false ) ;
}
}
}
/*
* Instead of operating directly on the input relation , we can
@ -6956,13 +6884,29 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
{
foreach ( lc , partially_grouped_rel - > pathlist )
{
ListCell * lc2 ;
Path * path = ( Path * ) lfirst ( lc ) ;
Path * path_save = path ;
List * pathkey_orderings = NIL ;
/* generate alternative group orderings that might be useful */
pathkey_orderings = get_useful_group_keys_orderings ( root , path ) ;
Assert ( list_length ( pathkey_orderings ) > 0 ) ;
/* process all potentially interesting grouping reorderings */
foreach ( lc2 , pathkey_orderings )
{
PathKeyInfo * info = ( PathKeyInfo * ) lfirst ( lc2 ) ;
/* restore the path (we replace it in the loop) */
path = path_save ;
path = make_ordered_path ( root ,
grouped_rel ,
path ,
partially_grouped_rel - > cheapest_total_path ,
root - > group_pathkeys ) ;
info - > pathkeys ) ;
if ( path = = NULL )
continue ;
@ -6975,7 +6919,7 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
grouped_rel - > reltarget ,
parse - > groupClause ? AGG_SORTED : AGG_PLAIN ,
AGGSPLIT_FINAL_DESERIAL ,
root - > pro cessed_groupC lause,
info - > clauses ,
havingQual ,
agg_final_costs ,
dNumGroups ) ) ;
@ -6984,13 +6928,14 @@ add_paths_to_grouping_rel(PlannerInfo *root, RelOptInfo *input_rel,
create_group_path ( root ,
grouped_rel ,
path ,
root - > pro cessed_groupC lause,
info - > clauses ,
havingQual ,
dNumGroups ) ) ;
}
}
}
}
if ( can_hash )
{
@ -7190,13 +7135,29 @@ create_partial_grouping_paths(PlannerInfo *root,
*/
foreach ( lc , input_rel - > pathlist )
{
ListCell * lc2 ;
Path * path = ( Path * ) lfirst ( lc ) ;
Path * path_save = path ;
List * pathkey_orderings = NIL ;
/* generate alternative group orderings that might be useful */
pathkey_orderings = get_useful_group_keys_orderings ( root , path ) ;
Assert ( list_length ( pathkey_orderings ) > 0 ) ;
/* process all potentially interesting grouping reorderings */
foreach ( lc2 , pathkey_orderings )
{
PathKeyInfo * info = ( PathKeyInfo * ) lfirst ( lc2 ) ;
/* restore the path (we replace it in the loop) */
path = path_save ;
path = make_ordered_path ( root ,
partially_grouped_rel ,
path ,
cheapest_total_path ,
root - > group_pathkeys ) ;
info - > pathkeys ) ;
if ( path = = NULL )
continue ;
@ -7209,7 +7170,7 @@ create_partial_grouping_paths(PlannerInfo *root,
partially_grouped_rel - > reltarget ,
parse - > groupClause ? AGG_SORTED : AGG_PLAIN ,
AGGSPLIT_INITIAL_SERIAL ,
root - > pro cessed_groupC lause,
info - > clauses ,
NIL ,
agg_partial_costs ,
dNumPartialGroups ) ) ;
@ -7218,24 +7179,42 @@ create_partial_grouping_paths(PlannerInfo *root,
create_group_path ( root ,
partially_grouped_rel ,
path ,
root - > pro cessed_groupC lause,
info - > clauses ,
NIL ,
dNumPartialGroups ) ) ;
}
}
}
if ( can_sort & & cheapest_partial_path ! = NULL )
{
/* Similar to above logic, but for partial paths. */
foreach ( lc , input_rel - > partial_pathlist )
{
ListCell * lc2 ;
Path * path = ( Path * ) lfirst ( lc ) ;
Path * path_save = path ;
List * pathkey_orderings = NIL ;
/* generate alternative group orderings that might be useful */
pathkey_orderings = get_useful_group_keys_orderings ( root , path ) ;
Assert ( list_length ( pathkey_orderings ) > 0 ) ;
/* process all potentially interesting grouping reorderings */
foreach ( lc2 , pathkey_orderings )
{
PathKeyInfo * info = ( PathKeyInfo * ) lfirst ( lc2 ) ;
/* restore the path (we replace it in the loop) */
path = path_save ;
path = make_ordered_path ( root ,
partially_grouped_rel ,
path ,
cheapest_partial_path ,
root - > group_pathkeys ) ;
info - > pathkeys ) ;
if ( path = = NULL )
continue ;
@ -7248,7 +7227,7 @@ create_partial_grouping_paths(PlannerInfo *root,
partially_grouped_rel - > reltarget ,
parse - > groupClause ? AGG_SORTED : AGG_PLAIN ,
AGGSPLIT_INITIAL_SERIAL ,
root - > pro cessed_groupC lause,
info - > clauses ,
NIL ,
agg_partial_costs ,
dNumPartialPartialGroups ) ) ;
@ -7257,11 +7236,12 @@ create_partial_grouping_paths(PlannerInfo *root,
create_group_path ( root ,
partially_grouped_rel ,
path ,
root - > pro cessed_groupC lause,
info - > clauses ,
NIL ,
dNumPartialPartialGroups ) ) ;
}
}
}
/*
* Add a partially - grouped HashAgg Path where possible
@ -7373,6 +7353,8 @@ gather_grouping_paths(PlannerInfo *root, RelOptInfo *rel)
* We can also skip the entire loop when we only have a single - item
* group_pathkeys because then we can ' t possibly have a presorted prefix
* of the list without having the list be fully sorted .
*
* XXX Shouldn ' t this also consider the group - key - reordering ?
*/
if ( ! enable_incremental_sort | | list_length ( root - > group_pathkeys ) = = 1 )
return ;