@ -1127,15 +1127,28 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
if ( parse - > hasTargetSRFs )
if ( parse - > hasTargetSRFs )
parse - > hasTargetSRFs = expression_returns_set ( ( Node * ) parse - > targetList ) ;
parse - > hasTargetSRFs = expression_returns_set ( ( Node * ) parse - > targetList ) ;
/*
* If we have grouping sets , expand the groupingSets tree of this query to
* a flat list of grouping sets . We need to do this before optimizing
* HAVING , since we can ' t easily tell if there ' s an empty grouping set
* until we have this representation .
*/
if ( parse - > groupingSets )
{
parse - > groupingSets =
expand_grouping_sets ( parse - > groupingSets , parse - > groupDistinct , - 1 ) ;
}
/*
/*
* In some cases we may want to transfer a HAVING clause into WHERE . We
* In some cases we may want to transfer a HAVING clause into WHERE . We
* cannot do so if the HAVING clause contains aggregates ( obviously ) or
* cannot do so if the HAVING clause contains aggregates ( obviously ) or
* volatile functions ( since a HAVING clause is supposed to be executed
* volatile functions ( since a HAVING clause is supposed to be executed
* only once per group ) . We also can ' t do this if there are any nonempty
* only once per group ) . We also can ' t do this if there are any grouping
* grouping sets and the clause references any columns that are nullable
* sets and the clause references any columns that are nullable by the
* by the grouping sets ; moving such a clause into WHERE would potentially
* grouping sets ; the nulled values of those columns are not available
* change the results . ( If there are only empty grouping sets , then the
* before the grouping step . ( The test on groupClause might seem wrong ,
* HAVING clause must be degenerate as discussed below . )
* but it ' s okay : it ' s just an optimization to avoid running pull_varnos
* when there cannot be any Vars in the HAVING clause . )
*
*
* Also , it may be that the clause is so expensive to execute that we ' re
* Also , it may be that the clause is so expensive to execute that we ' re
* better off doing it only once per group , despite the loss of
* better off doing it only once per group , despite the loss of
@ -1145,19 +1158,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
* clause into WHERE , in hopes of eliminating tuples before aggregation
* clause into WHERE , in hopes of eliminating tuples before aggregation
* instead of after .
* instead of after .
*
*
* If the query has explicit grouping then we can simply move such a
* If the query has no empty grouping set then we can simply move such a
* clause into WHERE ; any group that fails the clause will not be in the
* clause into WHERE ; any group that fails the clause will not be in the
* output because none of its tuples will reach the grouping or
* output because none of its tuples will reach the grouping or
* aggregation stage . Otherwise we must have a degenerate ( variable - free )
* aggregation stage . Otherwise we have to keep the clause in HAVING to
* HAVING clause , which we put in WHERE so that query_planner ( ) can use it
* ensure that we don ' t emit a bogus aggregated row . But then the HAVING
* in a gating Result node , but also keep in HAVING to ensure that we
* clause must be degenerate ( variable - free ) , so we can copy it into WHERE
* don ' t emit a bogus aggregated row . ( This could be done better , but it
* so that query_planner ( ) can use it in a gating Result node . ( This could
* seems not worth optimizing . )
* be done better , but it seems not worth optimizing . )
*
*
* Note that a HAVING clause may contain expressions that are not fully
* Note that a HAVING clause may contain expressions that are not fully
* preprocessed . This can happen if these expressions are part of
* preprocessed . This can happen if these expressions are part of
* grouping items . In such cases , they are replaced with GROUP Vars in
* grouping items . In such cases , they are replaced with GROUP Vars in
* the parser and then replaced back after we ' v e done with expression
* the parser and then replaced back after we ' r e done with expression
* preprocessing on havingQual . This is not an issue if the clause
* preprocessing on havingQual . This is not an issue if the clause
* remains in HAVING , because these expressions will be matched to lower
* remains in HAVING , because these expressions will be matched to lower
* target items in setrefs . c . However , if the clause is moved or copied
* target items in setrefs . c . However , if the clause is moved or copied
@ -1182,8 +1195,11 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
/* keep it in HAVING */
/* keep it in HAVING */
newHaving = lappend ( newHaving , havingclause ) ;
newHaving = lappend ( newHaving , havingclause ) ;
}
}
else if ( parse - > groupClause )
else if ( parse - > groupClause & &
( parse - > groupingSets = = NIL | |
( List * ) linitial ( parse - > groupingSets ) ! = NIL ) )
{
{
/* There is GROUP BY, but no empty grouping set */
Node * whereclause ;
Node * whereclause ;
/* Preprocess the HAVING clause fully */
/* Preprocess the HAVING clause fully */
@ -1196,6 +1212,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
}
}
else
else
{
{
/* There is an empty grouping set (perhaps implicitly) */
Node * whereclause ;
Node * whereclause ;
/* Preprocess the HAVING clause fully */
/* Preprocess the HAVING clause fully */
@ -2181,10 +2198,13 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
}
}
/*
/*
* Do preprocessing for groupingSets clause and related data . This handles the
* Do preprocessing for groupingSets clause and related data .
* preliminary steps of expanding the grouping sets , organizing them into lists
*
* of rollups , and preparing annotations which will later be filled in with
* We expect that parse - > groupingSets has already been expanded into a flat
* size estimates .
* list of grouping sets ( that is , just integer Lists of ressortgroupref
* numbers ) by expand_grouping_sets ( ) . This function handles the preliminary
* steps of organizing the grouping sets into lists of rollups , and preparing
* annotations which will later be filled in with size estimates .
*/
*/
static grouping_sets_data *
static grouping_sets_data *
preprocess_grouping_sets ( PlannerInfo * root )
preprocess_grouping_sets ( PlannerInfo * root )
@ -2195,19 +2215,18 @@ preprocess_grouping_sets(PlannerInfo *root)
ListCell * lc_set ;
ListCell * lc_set ;
grouping_sets_data * gd = palloc0 ( sizeof ( grouping_sets_data ) ) ;
grouping_sets_data * gd = palloc0 ( sizeof ( grouping_sets_data ) ) ;
parse - > groupingSets = expand_grouping_sets ( parse - > groupingSets , parse - > groupDistinct , - 1 ) ;
gd - > any_hashable = false ;
gd - > unhashable_refs = NULL ;
gd - > unsortable_refs = NULL ;
gd - > unsortable_sets = NIL ;
/*
/*
* We don ' t currently make any attempt to optimize the groupClause when
* We don ' t currently make any attempt to optimize the groupClause when
* there are grouping sets , so just duplicate it in processed_groupClause .
* there are grouping sets , so just duplicate it in processed_groupClause .
*/
*/
root - > processed_groupClause = parse - > groupClause ;
root - > processed_groupClause = parse - > groupClause ;
/* Detect unhashable and unsortable grouping expressions */
gd - > any_hashable = false ;
gd - > unhashable_refs = NULL ;
gd - > unsortable_refs = NULL ;
gd - > unsortable_sets = NIL ;
if ( parse - > groupClause )
if ( parse - > groupClause )
{
{
ListCell * lc ;
ListCell * lc ;