@ -110,6 +110,17 @@ typedef struct
int * tleref_to_colnum_map ;
} grouping_sets_data ;
/*
* Temporary structure for use during WindowClause reordering in order to be
* be able to sort WindowClauses on partitioning / ordering prefix .
*/
typedef struct
{
WindowClause * wc ;
List * uniqueOrder ; /* A List of unique ordering/partitioning
* clauses per Window */
} WindowClauseSortData ;
/* Local functions */
static Node * preprocess_expression ( PlannerInfo * root , Node * expr , int kind ) ;
static void preprocess_qual_conditions ( PlannerInfo * root , Node * jtnode ) ;
@ -236,6 +247,7 @@ static void create_partitionwise_grouping_paths(PlannerInfo *root,
static bool group_by_has_partkey ( RelOptInfo * input_rel ,
List * targetList ,
List * groupClause ) ;
static int common_prefix_cmp ( const void * a , const void * b ) ;
/*****************************************************************************
@ -5259,65 +5271,117 @@ postprocess_setop_tlist(List *new_tlist, List *orig_tlist)
static List *
select_active_windows ( PlannerInfo * root , WindowFuncLists * wflists )
{
List * result ;
List * actives ;
List * windowClause = root - > parse - > windowClause ;
List * result = NIL ;
ListCell * lc ;
int nActive = 0 ;
WindowClauseSortData * actives = palloc ( sizeof ( WindowClauseSortData )
* list_length ( windowClause ) ) ;
/* First, make a list of the active windows */
actives = NIL ;
foreach ( lc , root - > parse - > windowClause )
/* First, construct an array of the active windows */
foreach ( lc , windowClause )
{
WindowClause * wc = lfirst_node ( WindowClause , lc ) ;
/* It's only active if wflists shows some related WindowFuncs */
Assert ( wc - > winref < = wflists - > maxWinRef ) ;
if ( wflists - > windowFuncs [ wc - > winref ] ! = NIL )
actives = lappend ( actives , wc ) ;
if ( wflists - > windowFuncs [ wc - > winref ] = = NIL )
continue ;
actives [ nActive ] . wc = wc ; /* original clause */
/*
* For sorting , we want the list of partition keys followed by the
* list of sort keys . But pathkeys construction will remove duplicates
* between the two , so we can as well ( even though we can ' t detect all
* of the duplicates , since some may come from ECs - that might mean
* we miss optimization chances here ) . We must , however , ensure that
* the order of entries is preserved with respect to the ones we do
* keep .
*
* partitionClause and orderClause had their own duplicates removed in
* parse analysis , so we ' re only concerned here with removing
* orderClause entries that also appear in partitionClause .
*/
actives [ nActive ] . uniqueOrder =
list_concat_unique ( list_copy ( wc - > partitionClause ) ,
wc - > orderClause ) ;
nActive + + ;
}
/*
* Now , ensure that windows with identical partitioning / ordering clauses
* are adjacent in the list . This is required by the SQL standard , which
* says that only one sort is to be used for such windows , even if they
* are otherwise distinct ( eg , different names or framing clauses ) .
* Sort active windows by their partitioning / ordering clauses , ignoring
* any framing clauses , so that the windows that need the same sorting are
* adjacent in the list . When we come to generate paths , this will avoid
* inserting additional Sort nodes .
*
* This is how we implement a specific requirement from the SQL standard ,
* which says that when two or more windows are order - equivalent ( i . e .
* have matching partition and order clauses , even if their names or
* framing clauses differ ) , then all peer rows must be presented in the
* same order in all of them . If we allowed multiple sort nodes for such
* cases , we ' d risk having the peer rows end up in different orders in
* equivalent windows due to sort instability . ( See General Rule 4 of
* < window clause > in SQL2008 - SQL2016 . )
*
* There is room to be much smarter here , for example detecting whether
* one window ' s sort keys are a prefix of another ' s ( so that sorting for
* the latter would do for the former ) , or putting windows first that
* match a sort order available for the underlying query . For the moment
* we are content with meeting the spec .
* Additionally , if the entire list of clauses of one window is a prefix
* of another , put first the window with stronger sorting requirements .
* This way we will first sort for stronger window , and won ' t have to sort
* again for the weaker one .
*/
result = NIL ;
while ( actives ! = NIL )
{
WindowClause * wc = linitial_node ( WindowClause , actives ) ;
ListCell * prev ;
ListCell * next ;
qsort ( actives , nActive , sizeof ( WindowClauseSortData ) , common_prefix_cmp ) ;
/* Move wc from actives to result */
actives = list_delete_first ( actives ) ;
result = lappend ( result , wc ) ;
/* build ordered list of the original WindowClause nodes */
for ( int i = 0 ; i < nActive ; i + + )
result = lappend ( result , actives [ i ] . wc ) ;
/* Now move any matching windows from actives to result */
prev = NULL ;
for ( lc = list_head ( actives ) ; lc ; lc = next )
pfree ( actives ) ;
return result ;
}
/*
* common_prefix_cmp
* QSort comparison function for WindowClauseSortData
*
* Sort the windows by the required sorting clauses . First , compare the sort
* clauses themselves . Second , if one window ' s clauses are a prefix of another
* one ' s clauses , put the window with more sort clauses first .
*/
static int
common_prefix_cmp ( const void * a , const void * b )
{
WindowClause * wc2 = lfirst_node ( WindowClause , lc ) ;
const WindowClauseSortData * wcsa = a ;
const WindowClauseSortData * wcsb = b ;
ListCell * item_a ;
ListCell * item_b ;
next = lnext ( lc ) ;
/* framing options are NOT to be compared here! */
if ( equal ( wc - > partitionClause , wc2 - > partitionClause ) & &
equal ( wc - > orderClause , wc2 - > orderClause ) )
forboth ( item_a , wcsa - > uniqueOrder , item_b , wcsb - > uniqueOrder )
{
actives = list_delete_cell ( actives , lc , prev ) ;
result = lappend ( result , wc2 ) ;
}
else
prev = lc ;
}
SortGroupClause * sca = lfirst_node ( SortGroupClause , item_a ) ;
SortGroupClause * scb = lfirst_node ( SortGroupClause , item_b ) ;
if ( sca - > tleSortGroupRef > scb - > tleSortGroupRef )
return - 1 ;
else if ( sca - > tleSortGroupRef < scb - > tleSortGroupRef )
return 1 ;
else if ( sca - > sortop > scb - > sortop )
return - 1 ;
else if ( sca - > sortop < scb - > sortop )
return 1 ;
else if ( sca - > nulls_first & & ! scb - > nulls_first )
return - 1 ;
else if ( ! sca - > nulls_first & & scb - > nulls_first )
return 1 ;
/* no need to compare eqop, since it is fully determined by sortop */
}
return result ;
if ( list_length ( wcsa - > uniqueOrder ) > list_length ( wcsb - > uniqueOrder ) )
return - 1 ;
else if ( list_length ( wcsa - > uniqueOrder ) < list_length ( wcsb - > uniqueOrder ) )
return 1 ;
return 0 ;
}
/*