@ -152,12 +152,17 @@ static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
double limit_tuples ) ;
double limit_tuples ) ;
static Plan * prepare_sort_from_pathkeys ( PlannerInfo * root ,
static Plan * prepare_sort_from_pathkeys ( PlannerInfo * root ,
Plan * lefttree , List * pathkeys ,
Plan * lefttree , List * pathkeys ,
Relids relids ,
const AttrNumber * reqColIdx ,
bool adjust_tlist_in_place ,
bool adjust_tlist_in_place ,
int * p_numsortkeys ,
int * p_numsortkeys ,
AttrNumber * * p_sortColIdx ,
AttrNumber * * p_sortColIdx ,
Oid * * p_sortOperators ,
Oid * * p_sortOperators ,
Oid * * p_collations ,
Oid * * p_collations ,
bool * * p_nullsFirst ) ;
bool * * p_nullsFirst ) ;
static EquivalenceMember * find_ec_member_for_tle ( EquivalenceClass * ec ,
TargetEntry * tle ,
Relids relids ) ;
static Material * make_material ( Plan * lefttree ) ;
static Material * make_material ( Plan * lefttree ) ;
@ -706,6 +711,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
/* Compute sort column info, and adjust MergeAppend's tlist as needed */
/* Compute sort column info, and adjust MergeAppend's tlist as needed */
( void ) prepare_sort_from_pathkeys ( root , plan , pathkeys ,
( void ) prepare_sort_from_pathkeys ( root , plan , pathkeys ,
NULL ,
NULL ,
true ,
true ,
& node - > numCols ,
& node - > numCols ,
& node - > sortColIdx ,
& node - > sortColIdx ,
@ -733,6 +740,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
/* Compute sort column info, and adjust subplan's tlist as needed */
/* Compute sort column info, and adjust subplan's tlist as needed */
subplan = prepare_sort_from_pathkeys ( root , subplan , pathkeys ,
subplan = prepare_sort_from_pathkeys ( root , subplan , pathkeys ,
subpath - > parent - > relids ,
node - > sortColIdx ,
false ,
false ,
& numsortkeys ,
& numsortkeys ,
& sortColIdx ,
& sortColIdx ,
@ -2695,11 +2704,8 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
ListCell * indexpr_item ;
ListCell * indexpr_item ;
/*
/*
* Remove any PlaceHolderVars or binary - compatible relabeling of the
* Remove any binary - compatible relabeling of the indexkey
* indexkey ( this must match logic in match_index_to_operand ( ) ) .
*/
*/
while ( IsA ( node , PlaceHolderVar ) )
node = ( Node * ) ( ( PlaceHolderVar * ) node ) - > phexpr ;
if ( IsA ( node , RelabelType ) )
if ( IsA ( node , RelabelType ) )
node = ( Node * ) ( ( RelabelType * ) node ) - > arg ;
node = ( Node * ) ( ( RelabelType * ) node ) - > arg ;
@ -3515,55 +3521,6 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
return node ;
return node ;
}
}
/*
* add_sort_column - - - utility subroutine for building sort info arrays
*
* We need this routine because the same column might be selected more than
* once as a sort key column ; if so , the extra mentions are redundant .
*
* Caller is assumed to have allocated the arrays large enough for the
* max possible number of columns . Return value is the new column count .
*/
static int
add_sort_column ( AttrNumber colIdx , Oid sortOp , Oid coll , bool nulls_first ,
int numCols , AttrNumber * sortColIdx ,
Oid * sortOperators , Oid * collations , bool * nullsFirst )
{
int i ;
Assert ( OidIsValid ( sortOp ) ) ;
for ( i = 0 ; i < numCols ; i + + )
{
/*
* Note : we check sortOp because it ' s conceivable that " ORDER BY foo
* USING < , foo USING < < < " is not redundant, if <<< distinguishes
* values that < considers equal . We need not check nulls_first
* however because a lower - order column with the same sortop but
* opposite nulls direction is redundant .
*
* We could probably consider sort keys with the same sortop and
* different collations to be redundant too , but for the moment treat
* them as not redundant . This will be needed if we ever support
* collations with different notions of equality .
*/
if ( sortColIdx [ i ] = = colIdx & &
sortOperators [ numCols ] = = sortOp & &
collations [ numCols ] = = coll )
{
/* Already sorting by this col, so extra sort key is useless */
return numCols ;
}
}
/* Add the column */
sortColIdx [ numCols ] = colIdx ;
sortOperators [ numCols ] = sortOp ;
collations [ numCols ] = coll ;
nullsFirst [ numCols ] = nulls_first ;
return numCols + 1 ;
}
/*
/*
* prepare_sort_from_pathkeys
* prepare_sort_from_pathkeys
* Prepare to sort according to given pathkeys
* Prepare to sort according to given pathkeys
@ -3573,8 +3530,10 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
* plan targetlist if needed to add resjunk sort columns .
* plan targetlist if needed to add resjunk sort columns .
*
*
* Input parameters :
* Input parameters :
* ' lefttree ' is the node which yields input tuples
* ' lefttree ' is the plan node which yields input tuples
* ' pathkeys ' is the list of pathkeys by which the result is to be sorted
* ' pathkeys ' is the list of pathkeys by which the result is to be sorted
* ' relids ' identifies the child relation being sorted , if any
* ' reqColIdx ' is NULL or an array of required sort key column numbers
* ' adjust_tlist_in_place ' is TRUE if lefttree must be modified in - place
* ' adjust_tlist_in_place ' is TRUE if lefttree must be modified in - place
*
*
* We must convert the pathkey information into arrays of sort key column
* We must convert the pathkey information into arrays of sort key column
@ -3582,6 +3541,14 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
* which is the representation the executor wants . These are returned into
* which is the representation the executor wants . These are returned into
* the output parameters * p_numsortkeys etc .
* the output parameters * p_numsortkeys etc .
*
*
* When looking for matches to an EquivalenceClass ' s members , we will only
* consider child EC members if they match ' relids ' . This protects against
* possible incorrect matches to child expressions that contain no Vars .
*
* If reqColIdx isn ' t NULL then it contains sort key column numbers that
* we should match . This is used when making child plans for a MergeAppend ;
* it ' s an error if we can ' t match the columns .
*
* If the pathkeys include expressions that aren ' t simple Vars , we will
* If the pathkeys include expressions that aren ' t simple Vars , we will
* usually need to add resjunk items to the input plan ' s targetlist to
* usually need to add resjunk items to the input plan ' s targetlist to
* compute these expressions , since the Sort / MergeAppend node itself won ' t
* compute these expressions , since the Sort / MergeAppend node itself won ' t
@ -3596,6 +3563,8 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
*/
*/
static Plan *
static Plan *
prepare_sort_from_pathkeys ( PlannerInfo * root , Plan * lefttree , List * pathkeys ,
prepare_sort_from_pathkeys ( PlannerInfo * root , Plan * lefttree , List * pathkeys ,
Relids relids ,
const AttrNumber * reqColIdx ,
bool adjust_tlist_in_place ,
bool adjust_tlist_in_place ,
int * p_numsortkeys ,
int * p_numsortkeys ,
AttrNumber * * p_sortColIdx ,
AttrNumber * * p_sortColIdx ,
@ -3626,6 +3595,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
{
{
PathKey * pathkey = ( PathKey * ) lfirst ( i ) ;
PathKey * pathkey = ( PathKey * ) lfirst ( i ) ;
EquivalenceClass * ec = pathkey - > pk_eclass ;
EquivalenceClass * ec = pathkey - > pk_eclass ;
EquivalenceMember * em ;
TargetEntry * tle = NULL ;
TargetEntry * tle = NULL ;
Oid pk_datatype = InvalidOid ;
Oid pk_datatype = InvalidOid ;
Oid sortop ;
Oid sortop ;
@ -3645,16 +3615,41 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
Assert ( list_length ( ec - > ec_members ) = = 1 ) ;
Assert ( list_length ( ec - > ec_members ) = = 1 ) ;
pk_datatype = ( ( EquivalenceMember * ) linitial ( ec - > ec_members ) ) - > em_datatype ;
pk_datatype = ( ( EquivalenceMember * ) linitial ( ec - > ec_members ) ) - > em_datatype ;
}
}
else if ( reqColIdx ! = NULL )
{
/*
* If we are given a sort column number to match , only consider
* the single TLE at that position . It ' s possible that there
* is no such TLE , in which case fall through and generate a
* resjunk targetentry ( we assume this must have happened in the
* parent plan as well ) . If there is a TLE but it doesn ' t match
* the pathkey ' s EC , we do the same , which is probably the wrong
* thing but we ' ll leave it to caller to complain about the
* mismatch .
*/
tle = get_tle_by_resno ( tlist , reqColIdx [ numsortkeys ] ) ;
if ( tle )
{
em = find_ec_member_for_tle ( ec , tle , relids ) ;
if ( em )
{
/* found expr at right place in tlist */
pk_datatype = em - > em_datatype ;
}
else
tle = NULL ;
}
}
else
else
{
{
/*
/*
* Otherwise , we can sort by any non - constant expression listed in
* Otherwise , we can sort by any non - constant expression listed in
* the pathkey ' s EquivalenceClass . For now , we take the first one
* the pathkey ' s EquivalenceClass . For now , we take the first
* that corresponds to an available item in the tlist . If there
* tlist item found in the EC . If there ' s no match , we ' ll generat e
* isn ' t any , use the first one that is an expression in the
* a resjunk entry using the first EC member that is an expression
* input ' s vars . ( The non - const restriction only matters if the
* in the in put ' s vars . ( The non - const restriction only matters
* EC is below_outer_join ; but if it isn ' t , it won ' t contain
* if the EC is below_outer_join ; but if it isn ' t , it won ' t
* consts anyway , else we ' d have discarded the pathkey as
* contain con sts anyway , else we ' d have discarded the pathkey as
* redundant . )
* redundant . )
*
*
* XXX if we have a choice , is there any way of figuring out which
* XXX if we have a choice , is there any way of figuring out which
@ -3663,9 +3658,36 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
* in the same equivalence class . . . ) Not clear that we ever will
* in the same equivalence class . . . ) Not clear that we ever will
* have an interesting choice in practice , so it may not matter .
* have an interesting choice in practice , so it may not matter .
*/
*/
foreach ( j , tlist )
{
tle = ( TargetEntry * ) lfirst ( j ) ;
em = find_ec_member_for_tle ( ec , tle , relids ) ;
if ( em )
{
/* found expr already in tlist */
pk_datatype = em - > em_datatype ;
break ;
}
tle = NULL ;
}
}
if ( ! tle )
{
/*
* No matching tlist item ; look for a computable expression .
* Note that we treat Aggrefs as if they were variables ; this
* is necessary when attempting to sort the output from an Agg
* node for use in a WindowFunc ( since grouping_planner will
* have treated the Aggrefs as variables , too ) .
*/
Expr * sortexpr = NULL ;
foreach ( j , ec - > ec_members )
foreach ( j , ec - > ec_members )
{
{
EquivalenceMember * em = ( EquivalenceMember * ) lfirst ( j ) ;
EquivalenceMember * em = ( EquivalenceMember * ) lfirst ( j ) ;
List * exprvars ;
ListCell * k ;
/*
/*
* We shouldn ' t be trying to sort by an equivalence class that
* We shouldn ' t be trying to sort by an equivalence class that
@ -3675,91 +3697,56 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
if ( em - > em_is_const )
if ( em - > em_is_const )
continue ;
continue ;
tle = tlist_member ( ( Node * ) em - > em_expr , tlist ) ;
if ( tle )
{
pk_datatype = em - > em_datatype ;
break ; /* found expr already in tlist */
}
/*
/*
* We can also use it if the pathkey expression is a relabel
* Ignore child members unless they match the rel being sorted .
* of the tlist entry , or vice versa . This is needed for
* binary - compatible cases ( cf . make_pathkey_from_sortinfo ) .
* We prefer an exact match , though , so we do the basic search
* first .
*/
*/
tle = tlist_member_ignore_relabel ( ( Node * ) em - > em_expr , tlist ) ;
if ( em - > em_is_child & &
if ( tle )
! bms_equal ( em - > em_relids , relids ) )
continue ;
sortexpr = em - > em_expr ;
exprvars = pull_var_clause ( ( Node * ) sortexpr ,
PVC_INCLUDE_AGGREGATES ,
PVC_INCLUDE_PLACEHOLDERS ) ;
foreach ( k , exprvars )
{
if ( ! tlist_member_ignore_relabel ( lfirst ( k ) , tlist ) )
break ;
}
list_free ( exprvars ) ;
if ( ! k )
{
{
pk_datatype = em - > em_datatype ;
pk_datatype = em - > em_datatype ;
break ; /* found expr already in tlist */
break ; /* found usable expression */
}
}
}
}
if ( ! j )
elog ( ERROR , " could not find pathkey item to sort " ) ;
if ( ! tle )
/*
* Do we need to insert a Result node ?
*/
if ( ! adjust_tlist_in_place & &
! is_projection_capable_plan ( lefttree ) )
{
{
/*
/* copy needed so we don't modify input's tlist below */
* No matching tlist item ; look for a computable expression .
tlist = copyObject ( tlist ) ;
* Note that we treat Aggrefs as if they were variables ; this
lefttree = ( Plan * ) make_result ( root , tlist , NULL ,
* is necessary when attempting to sort the output from an Agg
lefttree ) ;
* node for use in a WindowFunc ( since grouping_planner will
}
* have treated the Aggrefs as variables , too ) .
*/
Expr * sortexpr = NULL ;
foreach ( j , ec - > ec_members )
{
EquivalenceMember * em = ( EquivalenceMember * ) lfirst ( j ) ;
List * exprvars ;
ListCell * k ;
if ( em - > em_is_const )
continue ;
sortexpr = em - > em_expr ;
exprvars = pull_var_clause ( ( Node * ) sortexpr ,
PVC_INCLUDE_AGGREGATES ,
PVC_INCLUDE_PLACEHOLDERS ) ;
foreach ( k , exprvars )
{
if ( ! tlist_member_ignore_relabel ( lfirst ( k ) , tlist ) )
break ;
}
list_free ( exprvars ) ;
if ( ! k )
{
pk_datatype = em - > em_datatype ;
break ; /* found usable expression */
}
}
if ( ! j )
elog ( ERROR , " could not find pathkey item to sort " ) ;
/*
* Do we need to insert a Result node ?
*/
if ( ! adjust_tlist_in_place & &
! is_projection_capable_plan ( lefttree ) )
{
/* copy needed so we don't modify input's tlist below */
tlist = copyObject ( tlist ) ;
lefttree = ( Plan * ) make_result ( root , tlist , NULL ,
lefttree ) ;
}
/* Don't bother testing is_projection_capable_plan again */
/* Don't bother testing is_projection_capable_plan again */
adjust_tlist_in_place = true ;
adjust_tlist_in_place = true ;
/*
/*
* Add resjunk entry to input ' s tlist
* Add resjunk entry to input ' s tlist
*/
*/
tle = makeTargetEntry ( sortexpr ,
tle = makeTargetEntry ( sortexpr ,
list_length ( tlist ) + 1 ,
list_length ( tlist ) + 1 ,
NULL ,
NULL ,
true ) ;
true ) ;
tlist = lappend ( tlist , tle ) ;
tlist = lappend ( tlist , tle ) ;
lefttree - > targetlist = tlist ; /* just in case NIL before */
lefttree - > targetlist = tlist ; /* just in case NIL before */
}
}
}
/*
/*
@ -3775,23 +3762,14 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
pathkey - > pk_strategy , pk_datatype , pk_datatype ,
pathkey - > pk_strategy , pk_datatype , pk_datatype ,
pathkey - > pk_opfamily ) ;
pathkey - > pk_opfamily ) ;
/*
/* Add the column to the sort arrays */
* The column might already be selected as a sort key , if the pathkeys
sortColIdx [ numsortkeys ] = tle - > resno ;
* contain duplicate entries . ( This can happen in scenarios where
sortOperators [ numsortkeys ] = sortop ;
* multiple mergejoinable clauses mention the same var , for example . )
collations [ numsortkeys ] = ec - > ec_collation ;
* So enter it only once in the sort arrays .
nullsFirst [ numsortkeys ] = pathkey - > pk_nulls_first ;
*/
numsortkeys + + ;
numsortkeys = add_sort_column ( tle - > resno ,
sortop ,
pathkey - > pk_eclass - > ec_collation ,
pathkey - > pk_nulls_first ,
numsortkeys ,
sortColIdx , sortOperators ,
collations , nullsFirst ) ;
}
}
Assert ( numsortkeys > 0 ) ;
/* Return results */
/* Return results */
* p_numsortkeys = numsortkeys ;
* p_numsortkeys = numsortkeys ;
* p_sortColIdx = sortColIdx ;
* p_sortColIdx = sortColIdx ;
@ -3802,6 +3780,57 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
return lefttree ;
return lefttree ;
}
}
/*
* find_ec_member_for_tle
* Locate an EquivalenceClass member matching the given TLE , if any
*
* Child EC members are ignored unless they match ' relids ' .
*/
static EquivalenceMember *
find_ec_member_for_tle ( EquivalenceClass * ec ,
TargetEntry * tle ,
Relids relids )
{
Expr * tlexpr ;
ListCell * lc ;
/* We ignore binary-compatible relabeling on both ends */
tlexpr = tle - > expr ;
while ( tlexpr & & IsA ( tlexpr , RelabelType ) )
tlexpr = ( ( RelabelType * ) tlexpr ) - > arg ;
foreach ( lc , ec - > ec_members )
{
EquivalenceMember * em = ( EquivalenceMember * ) lfirst ( lc ) ;
Expr * emexpr ;
/*
* We shouldn ' t be trying to sort by an equivalence class that
* contains a constant , so no need to consider such cases any
* further .
*/
if ( em - > em_is_const )
continue ;
/*
* Ignore child members unless they match the rel being sorted .
*/
if ( em - > em_is_child & &
! bms_equal ( em - > em_relids , relids ) )
continue ;
/* Match if same expression (after stripping relabel) */
emexpr = em - > em_expr ;
while ( emexpr & & IsA ( emexpr , RelabelType ) )
emexpr = ( ( RelabelType * ) emexpr ) - > arg ;
if ( equal ( emexpr , tlexpr ) )
return em ;
}
return NULL ;
}
/*
/*
* make_sort_from_pathkeys
* make_sort_from_pathkeys
* Create sort plan to sort according to given pathkeys
* Create sort plan to sort according to given pathkeys
@ -3823,6 +3852,8 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
/* Compute sort column info, and adjust lefttree as needed */
/* Compute sort column info, and adjust lefttree as needed */
lefttree = prepare_sort_from_pathkeys ( root , lefttree , pathkeys ,
lefttree = prepare_sort_from_pathkeys ( root , lefttree , pathkeys ,
NULL ,
NULL ,
false ,
false ,
& numsortkeys ,
& numsortkeys ,
& sortColIdx ,
& sortColIdx ,
@ -3854,9 +3885,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
Oid * collations ;
Oid * collations ;
bool * nullsFirst ;
bool * nullsFirst ;
/*
/* Convert list-ish representation to arrays wanted by executor */
* We will need at most list_length ( sortcls ) sort columns ; possibly less
*/
numsortkeys = list_length ( sortcls ) ;
numsortkeys = list_length ( sortcls ) ;
sortColIdx = ( AttrNumber * ) palloc ( numsortkeys * sizeof ( AttrNumber ) ) ;
sortColIdx = ( AttrNumber * ) palloc ( numsortkeys * sizeof ( AttrNumber ) ) ;
sortOperators = ( Oid * ) palloc ( numsortkeys * sizeof ( Oid ) ) ;
sortOperators = ( Oid * ) palloc ( numsortkeys * sizeof ( Oid ) ) ;
@ -3864,27 +3893,18 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
nullsFirst = ( bool * ) palloc ( numsortkeys * sizeof ( bool ) ) ;
nullsFirst = ( bool * ) palloc ( numsortkeys * sizeof ( bool ) ) ;
numsortkeys = 0 ;
numsortkeys = 0 ;
foreach ( l , sortcls )
foreach ( l , sortcls )
{
{
SortGroupClause * sortcl = ( SortGroupClause * ) lfirst ( l ) ;
SortGroupClause * sortcl = ( SortGroupClause * ) lfirst ( l ) ;
TargetEntry * tle = get_sortgroupclause_tle ( sortcl , sub_tlist ) ;
TargetEntry * tle = get_sortgroupclause_tle ( sortcl , sub_tlist ) ;
/*
sortColIdx [ numsortkeys ] = tle - > resno ;
* Check for the possibility of duplicate order - by clauses - - - the
sortOperators [ numsortkeys ] = sortcl - > sortop ;
* parser should have removed ' em , but no point in sorting
collations [ numsortkeys ] = exprCollation ( ( Node * ) tle - > expr ) ;
* redundantly .
nullsFirst [ numsortkeys ] = sortcl - > nulls_first ;
*/
numsortkeys + + ;
numsortkeys = add_sort_column ( tle - > resno , sortcl - > sortop ,
exprCollation ( ( Node * ) tle - > expr ) ,
sortcl - > nulls_first ,
numsortkeys ,
sortColIdx , sortOperators ,
collations , nullsFirst ) ;
}
}
Assert ( numsortkeys > 0 ) ;
return make_sort ( root , lefttree , numsortkeys ,
return make_sort ( root , lefttree , numsortkeys ,
sortColIdx , sortOperators , collations ,
sortColIdx , sortOperators , collations ,
nullsFirst , - 1.0 ) ;
nullsFirst , - 1.0 ) ;
@ -3910,7 +3930,6 @@ make_sort_from_groupcols(PlannerInfo *root,
Plan * lefttree )
Plan * lefttree )
{
{
List * sub_tlist = lefttree - > targetlist ;
List * sub_tlist = lefttree - > targetlist ;
int grpno = 0 ;
ListCell * l ;
ListCell * l ;
int numsortkeys ;
int numsortkeys ;
AttrNumber * sortColIdx ;
AttrNumber * sortColIdx ;
@ -3918,9 +3937,7 @@ make_sort_from_groupcols(PlannerInfo *root,
Oid * collations ;
Oid * collations ;
bool * nullsFirst ;
bool * nullsFirst ;
/*
/* Convert list-ish representation to arrays wanted by executor */
* We will need at most list_length ( groupcls ) sort columns ; possibly less
*/
numsortkeys = list_length ( groupcls ) ;
numsortkeys = list_length ( groupcls ) ;
sortColIdx = ( AttrNumber * ) palloc ( numsortkeys * sizeof ( AttrNumber ) ) ;
sortColIdx = ( AttrNumber * ) palloc ( numsortkeys * sizeof ( AttrNumber ) ) ;
sortOperators = ( Oid * ) palloc ( numsortkeys * sizeof ( Oid ) ) ;
sortOperators = ( Oid * ) palloc ( numsortkeys * sizeof ( Oid ) ) ;
@ -3928,28 +3945,18 @@ make_sort_from_groupcols(PlannerInfo *root,
nullsFirst = ( bool * ) palloc ( numsortkeys * sizeof ( bool ) ) ;
nullsFirst = ( bool * ) palloc ( numsortkeys * sizeof ( bool ) ) ;
numsortkeys = 0 ;
numsortkeys = 0 ;
foreach ( l , groupcls )
foreach ( l , groupcls )
{
{
SortGroupClause * grpcl = ( SortGroupClause * ) lfirst ( l ) ;
SortGroupClause * grpcl = ( SortGroupClause * ) lfirst ( l ) ;
TargetEntry * tle = get_tle_by_resno ( sub_tlist , grpColIdx [ grp no] ) ;
TargetEntry * tle = get_tle_by_resno ( sub_tlist , grpColIdx [ nums ortkeys ] ) ;
/*
sortColIdx [ numsortkeys ] = tle - > resno ;
* Check for the possibility of duplicate group - by clauses - - - the
sortOperators [ numsortkeys ] = grpcl - > sortop ;
* parser should have removed ' em , but no point in sorting
collations [ numsortkeys ] = exprCollation ( ( Node * ) tle - > expr ) ;
* redundantly .
nullsFirst [ numsortkeys ] = grpcl - > nulls_first ;
*/
numsortkeys + + ;
numsortkeys = add_sort_column ( tle - > resno , grpcl - > sortop ,
exprCollation ( ( Node * ) tle - > expr ) ,
grpcl - > nulls_first ,
numsortkeys ,
sortColIdx , sortOperators ,
collations , nullsFirst ) ;
grpno + + ;
}
}
Assert ( numsortkeys > 0 ) ;
return make_sort ( root , lefttree , numsortkeys ,
return make_sort ( root , lefttree , numsortkeys ,
sortColIdx , sortOperators , collations ,
sortColIdx , sortOperators , collations ,
nullsFirst , - 1.0 ) ;
nullsFirst , - 1.0 ) ;