@ -44,6 +44,7 @@
# include "optimizer/tlist.h"
# include "parser/parse_clause.h"
# include "parser/parsetree.h"
# include "partitioning/partbounds.h"
# include "partitioning/partprune.h"
# include "rewrite/rewriteManip.h"
# include "utils/lsyscache.h"
@ -96,15 +97,16 @@ static void set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
Index rti , RangeTblEntry * rte ) ;
static void set_append_rel_pathlist ( PlannerInfo * root , RelOptInfo * rel ,
Index rti , RangeTblEntry * rte ) ;
static void generate_merge append_paths ( PlannerInfo * root , RelOptInfo * rel ,
List * live_childrels ,
List * all_child_pathkeys ,
List * partitioned_rels ) ;
static void generate_ordered append_paths ( PlannerInfo * root , RelOptInfo * rel ,
List * live_childrels ,
List * all_child_pathkeys ,
List * partitioned_rels ) ;
static Path * get_cheapest_parameterized_child_path ( PlannerInfo * root ,
RelOptInfo * rel ,
Relids required_outer ) ;
static void accumulate_append_subpath ( Path * path ,
List * * subpaths , List * * special_subpaths ) ;
static Path * get_singleton_append_subpath ( Path * path ) ;
static void set_dummy_rel_pathlist ( RelOptInfo * rel ) ;
static void set_subquery_pathlist ( PlannerInfo * root , RelOptInfo * rel ,
Index rti , RangeTblEntry * rte ) ;
@ -1520,7 +1522,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
*/
if ( subpaths_valid )
add_path ( rel , ( Path * ) create_append_path ( root , rel , subpaths , NIL ,
NULL , 0 , false ,
NIL , NULL , 0 , false ,
partitioned_rels , - 1 ) ) ;
/*
@ -1562,7 +1564,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
/* Generate a partial append path. */
appendpath = create_append_path ( root , rel , NIL , partial_subpaths ,
NULL , parallel_workers ,
NIL , NULL , parallel_workers ,
enable_parallel_append ,
partitioned_rels , - 1 ) ;
@ -1612,19 +1614,19 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
appendpath = create_append_path ( root , rel , pa_nonpartial_subpaths ,
pa_partial_subpaths ,
NULL , parallel_workers , true ,
NIL , NULL , parallel_workers , true ,
partitioned_rels , partial_rows ) ;
add_partial_path ( rel , ( Path * ) appendpath ) ;
}
/*
* Also build unparameterized MergeA ppend paths based on the collected
* Also build unparameterized ordered a ppend paths based on the collected
* list of child pathkeys .
*/
if ( subpaths_valid )
generate_merge append_paths ( root , rel , live_childrels ,
all_child_pathkeys ,
partitioned_rels ) ;
generate_ordered append_paths ( root , rel , live_childrels ,
all_child_pathkeys ,
partitioned_rels ) ;
/*
* Build Append paths for each parameterization seen among the child rels .
@ -1674,7 +1676,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
if ( subpaths_valid )
add_path ( rel , ( Path * )
create_append_path ( root , rel , subpaths , NIL ,
required_outer , 0 , false ,
NIL , required_outer , 0 , false ,
partitioned_rels , - 1 ) ) ;
}
@ -1703,8 +1705,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
continue ;
appendpath = create_append_path ( root , rel , NIL , list_make1 ( path ) ,
NULL , path - > parallel_workers ,
true ,
NIL , NULL ,
path - > parallel_workers , true ,
partitioned_rels , partial_rows ) ;
add_partial_path ( rel , ( Path * ) appendpath ) ;
}
@ -1712,17 +1714,21 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
}
/*
* generate_merge append_paths
* Generate MergeA ppend paths for an append relation
* generate_ordered append_paths
* Generate ordered a ppend paths for an append relation
*
* Generate a path for each ordering ( pathkey list ) appearing in
* Usually we generate MergeAppend paths here , but there are some special
* cases where we can generate simple Append paths , because the subpaths
* can provide tuples in the required order already .
*
* We generate a path for each ordering ( pathkey list ) appearing in
* all_child_pathkeys .
*
* We consider both cheapest - startup and cheapest - total cases , ie , for each
* interesting ordering , collect all the cheapest startup subpaths and all the
* cheapest total paths , and build a MergeAppend path for each case .
* cheapest total paths , and build a suitable path for each case .
*
* We don ' t currently generate any parameterized MergeAppend paths . While
* We don ' t currently generate any parameterized ordered paths here . While
* it would not take much more code here to do so , it ' s very unclear that it
* is worth the planning cycles to investigate such paths : there ' s little
* use for an ordered path on the inside of a nestloop . In fact , it ' s likely
@ -1731,17 +1737,52 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
* and a parameterized MergeAppend is going to be more expensive than the
* corresponding parameterized Append path . If we ever try harder to support
* parameterized mergejoin plans , it might be worth adding support for
* parameterized MergeAppends to feed such joins . ( See notes in
* parameterized paths here to feed such joins . ( See notes in
* optimizer / README for why that might not ever happen , though . )
*/
static void
generate_merge append_paths ( PlannerInfo * root , RelOptInfo * rel ,
List * live_childrels ,
List * all_child_pathkeys ,
List * partitioned_rels )
generate_ordered append_paths ( PlannerInfo * root , RelOptInfo * rel ,
List * live_childrels ,
List * all_child_pathkeys ,
List * partitioned_rels )
{
ListCell * lcp ;
List * partition_pathkeys = NIL ;
List * partition_pathkeys_desc = NIL ;
bool partition_pathkeys_partial = true ;
bool partition_pathkeys_desc_partial = true ;
/*
* Some partitioned table setups may allow us to use an Append node
* instead of a MergeAppend . This is possible in cases such as RANGE
* partitioned tables where it ' s guaranteed that an earlier partition must
* contain rows which come earlier in the sort order . To detect whether
* this is relevant , build pathkey descriptions of the partition ordering ,
* for both forward and reverse scans .
*/
if ( rel - > part_scheme ! = NULL & & IS_SIMPLE_REL ( rel ) & &
partitions_are_ordered ( rel - > boundinfo , rel - > nparts ) )
{
partition_pathkeys = build_partition_pathkeys ( root , rel ,
ForwardScanDirection ,
& partition_pathkeys_partial ) ;
partition_pathkeys_desc = build_partition_pathkeys ( root , rel ,
BackwardScanDirection ,
& partition_pathkeys_desc_partial ) ;
/*
* You might think we should truncate_useless_pathkeys here , but
* allowing partition keys which are a subset of the query ' s pathkeys
* can often be useful . For example , consider a table partitioned by
* RANGE ( a , b ) , and a query with ORDER BY a , b , c . If we have child
* paths that can produce the a , b , c ordering ( perhaps via indexes on
* ( a , b , c ) ) then it works to consider the appendrel output as
* ordered by a , b , c .
*/
}
/* Now consider each interesting sort ordering */
foreach ( lcp , all_child_pathkeys )
{
List * pathkeys = ( List * ) lfirst ( lcp ) ;
@ -1749,6 +1790,27 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
List * total_subpaths = NIL ;
bool startup_neq_total = false ;
ListCell * lcr ;
bool match_partition_order ;
bool match_partition_order_desc ;
/*
* Determine if this sort ordering matches any partition pathkeys we
* have , for both ascending and descending partition order . If the
* partition pathkeys happen to be contained in pathkeys then it still
* works , as described above , providing that the partition pathkeys
* are complete and not just a prefix of the partition keys . ( In such
* cases we ' ll be relying on the child paths to have sorted the
* lower - order columns of the required pathkeys . )
*/
match_partition_order =
pathkeys_contained_in ( pathkeys , partition_pathkeys ) | |
( ! partition_pathkeys_partial & &
pathkeys_contained_in ( partition_pathkeys , pathkeys ) ) ;
match_partition_order_desc = ! match_partition_order & &
( pathkeys_contained_in ( pathkeys , partition_pathkeys_desc ) | |
( ! partition_pathkeys_desc_partial & &
pathkeys_contained_in ( partition_pathkeys_desc , pathkeys ) ) ) ;
/* Select the child paths for this ordering... */
foreach ( lcr , live_childrels )
@ -1791,26 +1853,94 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
if ( cheapest_startup ! = cheapest_total )
startup_neq_total = true ;
accumulate_append_subpath ( cheapest_startup ,
& startup_subpaths , NULL ) ;
accumulate_append_subpath ( cheapest_total ,
& total_subpaths , NULL ) ;
/*
* Collect the appropriate child paths . The required logic varies
* for the Append and MergeAppend cases .
*/
if ( match_partition_order )
{
/*
* We ' re going to make a plain Append path . We don ' t need
* most of what accumulate_append_subpath would do , but we do
* want to cut out child Appends or MergeAppends if they have
* just a single subpath ( and hence aren ' t doing anything
* useful ) .
*/
cheapest_startup = get_singleton_append_subpath ( cheapest_startup ) ;
cheapest_total = get_singleton_append_subpath ( cheapest_total ) ;
startup_subpaths = lappend ( startup_subpaths , cheapest_startup ) ;
total_subpaths = lappend ( total_subpaths , cheapest_total ) ;
}
else if ( match_partition_order_desc )
{
/*
* As above , but we need to reverse the order of the children ,
* because nodeAppend . c doesn ' t know anything about reverse
* ordering and will scan the children in the order presented .
*/
cheapest_startup = get_singleton_append_subpath ( cheapest_startup ) ;
cheapest_total = get_singleton_append_subpath ( cheapest_total ) ;
startup_subpaths = lcons ( cheapest_startup , startup_subpaths ) ;
total_subpaths = lcons ( cheapest_total , total_subpaths ) ;
}
else
{
/*
* Otherwise , rely on accumulate_append_subpath to collect the
* child paths for the MergeAppend .
*/
accumulate_append_subpath ( cheapest_startup ,
& startup_subpaths , NULL ) ;
accumulate_append_subpath ( cheapest_total ,
& total_subpaths , NULL ) ;
}
}
/* ... and build the MergeAppend paths */
add_path ( rel , ( Path * ) create_merge_append_path ( root ,
rel ,
startup_subpaths ,
pathkeys ,
NULL ,
partitioned_rels ) ) ;
if ( startup_neq_total )
/* ... and build the Append or MergeAppend paths */
if ( match_partition_order | | match_partition_order_desc )
{
/* We only need Append */
add_path ( rel , ( Path * ) create_append_path ( root ,
rel ,
startup_subpaths ,
NIL ,
pathkeys ,
NULL ,
0 ,
false ,
partitioned_rels ,
- 1 ) ) ;
if ( startup_neq_total )
add_path ( rel , ( Path * ) create_append_path ( root ,
rel ,
total_subpaths ,
NIL ,
pathkeys ,
NULL ,
0 ,
false ,
partitioned_rels ,
- 1 ) ) ;
}
else
{
/* We need MergeAppend */
add_path ( rel , ( Path * ) create_merge_append_path ( root ,
rel ,
total_subpaths ,
startup _subpaths,
pathkeys ,
NULL ,
partitioned_rels ) ) ;
if ( startup_neq_total )
add_path ( rel , ( Path * ) create_merge_append_path ( root ,
rel ,
total_subpaths ,
pathkeys ,
NULL ,
partitioned_rels ) ) ;
}
}
}
@ -1901,7 +2031,6 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
* omitting a sort step , which seems fine : if the parent is to be an Append ,
* its result would be unsorted anyway , while if the parent is to be a
* MergeAppend , there ' s no point in a separate sort on a child .
* its result would be unsorted anyway .
*
* Normally , either path is a partial path and subpaths is a list of partial
* paths , or else path is a non - partial plan and subpaths is a list of those .
@ -1951,6 +2080,36 @@ accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths)
* subpaths = lappend ( * subpaths , path ) ;
}
/*
* get_singleton_append_subpath
* Returns the single subpath of an Append / MergeAppend , or just
* return ' path ' if it ' s not a single sub - path Append / MergeAppend .
*
* Note : ' path ' must not be a parallel - aware path .
*/
static Path *
get_singleton_append_subpath ( Path * path )
{
Assert ( ! path - > parallel_aware ) ;
if ( IsA ( path , AppendPath ) )
{
AppendPath * apath = ( AppendPath * ) path ;
if ( list_length ( apath - > subpaths ) = = 1 )
return ( Path * ) linitial ( apath - > subpaths ) ;
}
else if ( IsA ( path , MergeAppendPath ) )
{
MergeAppendPath * mpath = ( MergeAppendPath * ) path ;
if ( list_length ( mpath - > subpaths ) = = 1 )
return ( Path * ) linitial ( mpath - > subpaths ) ;
}
return path ;
}
/*
* set_dummy_rel_pathlist
* Build a dummy path for a relation that ' s been excluded by constraints
@ -1975,7 +2134,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
/* Set up the dummy path */
add_path ( rel , ( Path * ) create_append_path ( NULL , rel , NIL , NIL ,
rel - > lateral_relids ,
NIL , rel - > lateral_relids ,
0 , false , NIL , - 1 ) ) ;
/*