OSDN Git Service

Support PostgreSQL 13.
[pghintplan/pg_hint_plan.git] / core.c
diff --git a/core.c b/core.c
index d809fe7..986ba97 100644 (file)
--- a/core.c
+++ b/core.c
@@ -38,6 +38,8 @@
  *     restriction_is_constant_false()
  *     update_child_rel_info()
  *     build_child_join_sjinfo()
+ *     get_matching_part_pairs()
+ *     compute_partition_bounds()
  *
  *
  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
@@ -137,7 +139,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                if (rel->part_scheme)
                        rel->partitioned_child_rels =
                                list_concat(rel->partitioned_child_rels,
-                                                       list_copy(childrel->partitioned_child_rels));
+                                                       childrel->partitioned_child_rels);
 
                /*
                 * Child is live, so add it to the live_childrels list for use below.
@@ -239,7 +241,7 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
                         * once we know the final targetlist (see grouping_planner).
                         */
                        if (lev < levels_needed)
-                               generate_gather_paths(root, rel, false);
+                               generate_useful_gather_paths(root, rel, false);
 
                        /* Find and save the cheapest paths for this rel */
                        set_cheapest(rel);
@@ -337,15 +339,23 @@ join_search_one_level(PlannerInfo *root, int level)
                         * to each initial rel they don't already include but have a join
                         * clause or restriction with.
                         */
+                       List       *other_rels_list;
                        ListCell   *other_rels;
 
                        if (level == 2)         /* consider remaining initial rels */
-                               other_rels = lnext(r);
+                       {
+                               other_rels_list = joinrels[level - 1];
+                               other_rels = lnext(other_rels_list, r);
+                       }
                        else                            /* consider all initial rels */
-                               other_rels = list_head(joinrels[1]);
+                       {
+                               other_rels_list = joinrels[1];
+                               other_rels = list_head(other_rels_list);
+                       }
 
                        make_rels_by_clause_joins(root,
                                                                          old_rel,
+                                                                         other_rels_list,
                                                                          other_rels);
                }
                else
@@ -364,7 +374,7 @@ join_search_one_level(PlannerInfo *root, int level)
                         */
                        make_rels_by_clauseless_joins(root,
                                                                                  old_rel,
-                                                                                 list_head(joinrels[1]));
+                                                                                 joinrels[1]);
                }
        }
 
@@ -390,6 +400,7 @@ join_search_one_level(PlannerInfo *root, int level)
                foreach(r, joinrels[k])
                {
                        RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
+                       List       *other_rels_list;
                        ListCell   *other_rels;
                        ListCell   *r2;
 
@@ -403,11 +414,18 @@ join_search_one_level(PlannerInfo *root, int level)
                                continue;
 
                        if (k == other_level)
-                               other_rels = lnext(r);  /* only consider remaining rels */
+                       {
+                               /* only consider remaining rels */
+                               other_rels_list = joinrels[k];
+                               other_rels = lnext(other_rels_list, r);
+                       }
                        else
-                               other_rels = list_head(joinrels[other_level]);
+                       {
+                               other_rels_list = joinrels[other_level];
+                               other_rels = list_head(other_rels_list);
+                       }
 
-                       for_each_cell(r2, other_rels)
+                       for_each_cell(r2, other_rels_list, other_rels)
                        {
                                RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
 
@@ -459,7 +477,7 @@ join_search_one_level(PlannerInfo *root, int level)
 
                        make_rels_by_clauseless_joins(root,
                                                                                  old_rel,
-                                                                                 list_head(joinrels[1]));
+                                                                                 joinrels[1]);
                }
 
                /*----------
@@ -502,8 +520,9 @@ join_search_one_level(PlannerInfo *root, int level)
  * automatically ensures that each new joinrel is only added to the list once.
  *
  * 'old_rel' is the relation entry for the relation to be joined
- * 'other_rels': the first cell in a linked list containing the other
+ * 'other_rels_list': a list containing the other
  * rels to be considered for joining
+ * 'other_rels': the first cell to be considered
  *
  * Currently, this is only used with initial rels in other_rels, but it
  * will work for joining to joinrels too.
@@ -511,11 +530,12 @@ join_search_one_level(PlannerInfo *root, int level)
 static void
 make_rels_by_clause_joins(PlannerInfo *root,
                                                  RelOptInfo *old_rel,
+                                                 List *other_rels_list,
                                                  ListCell *other_rels)
 {
        ListCell   *l;
 
-       for_each_cell(l, other_rels)
+       for_each_cell(l, other_rels_list, other_rels)
        {
                RelOptInfo *other_rel = (RelOptInfo *) lfirst(l);
 
@@ -537,8 +557,7 @@ make_rels_by_clause_joins(PlannerInfo *root,
  *       The join rels are returned in root->join_rel_level[join_cur_level].
  *
  * 'old_rel' is the relation entry for the relation to be joined
- * 'other_rels': the first cell of a linked list containing the
- * other rels to be considered for joining
+ * 'other_rels': a list containing the other rels to be considered for joining
  *
  * Currently, this is only used with initial rels in other_rels, but it would
  * work for joining to joinrels too.
@@ -546,11 +565,11 @@ make_rels_by_clause_joins(PlannerInfo *root,
 static void
 make_rels_by_clauseless_joins(PlannerInfo *root,
                                                          RelOptInfo *old_rel,
-                                                         ListCell *other_rels)
+                                                         List *other_rels)
 {
        ListCell   *l;
 
-       for_each_cell(l, other_rels)
+       foreach(l, other_rels)
        {
                RelOptInfo *other_rel = (RelOptInfo *) lfirst(l);
 
@@ -1025,6 +1044,196 @@ build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
 }
 
 /*
+ * get_matching_part_pairs
+ *             Generate pairs of partitions to be joined from inputs
+ */
+static void
+get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
+                                               RelOptInfo *rel1, RelOptInfo *rel2,
+                                               List **parts1, List **parts2)
+{
+       bool            rel1_is_simple = IS_SIMPLE_REL(rel1);
+       bool            rel2_is_simple = IS_SIMPLE_REL(rel2);
+       int                     cnt_parts;
+
+       *parts1 = NIL;
+       *parts2 = NIL;
+
+       for (cnt_parts = 0; cnt_parts < joinrel->nparts; cnt_parts++)
+       {
+               RelOptInfo *child_joinrel = joinrel->part_rels[cnt_parts];
+               RelOptInfo *child_rel1;
+               RelOptInfo *child_rel2;
+               Relids          child_relids1;
+               Relids          child_relids2;
+
+               /*
+                * If this segment of the join is empty, it means that this segment
+                * was ignored when previously creating child-join paths for it in
+                * try_partitionwise_join() as it would not contribute to the join
+                * result, due to one or both inputs being empty; add NULL to each of
+                * the given lists so that this segment will be ignored again in that
+                * function.
+                */
+               if (!child_joinrel)
+               {
+                       *parts1 = lappend(*parts1, NULL);
+                       *parts2 = lappend(*parts2, NULL);
+                       continue;
+               }
+
+               /*
+                * Get a relids set of partition(s) involved in this join segment that
+                * are from the rel1 side.
+                */
+               child_relids1 = bms_intersect(child_joinrel->relids,
+                                                                         rel1->all_partrels);
+               Assert(bms_num_members(child_relids1) == bms_num_members(rel1->relids));
+
+               /*
+                * Get a child rel for rel1 with the relids.  Note that we should have
+                * the child rel even if rel1 is a join rel, because in that case the
+                * partitions specified in the relids would have matching/overlapping
+                * boundaries, so the specified partitions should be considered as
+                * ones to be joined when planning partitionwise joins of rel1,
+                * meaning that the child rel would have been built by the time we get
+                * here.
+                */
+               if (rel1_is_simple)
+               {
+                       int                     varno = bms_singleton_member(child_relids1);
+
+                       child_rel1 = find_base_rel(root, varno);
+               }
+               else
+                       child_rel1 = find_join_rel(root, child_relids1);
+               Assert(child_rel1);
+
+               /*
+                * Get a relids set of partition(s) involved in this join segment that
+                * are from the rel2 side.
+                */
+               child_relids2 = bms_intersect(child_joinrel->relids,
+                                                                         rel2->all_partrels);
+               Assert(bms_num_members(child_relids2) == bms_num_members(rel2->relids));
+
+               /*
+                * Get a child rel for rel2 with the relids.  See above comments.
+                */
+               if (rel2_is_simple)
+               {
+                       int                     varno = bms_singleton_member(child_relids2);
+
+                       child_rel2 = find_base_rel(root, varno);
+               }
+               else
+                       child_rel2 = find_join_rel(root, child_relids2);
+               Assert(child_rel2);
+
+               /*
+                * The join of rel1 and rel2 is legal, so is the join of the child
+                * rels obtained above; add them to the given lists as a join pair
+                * producing this join segment.
+                */
+               *parts1 = lappend(*parts1, child_rel1);
+               *parts2 = lappend(*parts2, child_rel2);
+       }
+}
+
+
+/*
+ * compute_partition_bounds
+ *             Compute the partition bounds for a join rel from those for inputs
+ */
+static void
+compute_partition_bounds(PlannerInfo *root, RelOptInfo *rel1,
+                                                RelOptInfo *rel2, RelOptInfo *joinrel,
+                                                SpecialJoinInfo *parent_sjinfo,
+                                                List **parts1, List **parts2)
+{
+       /*
+        * If we don't have the partition bounds for the join rel yet, try to
+        * compute those along with pairs of partitions to be joined.
+        */
+       if (joinrel->nparts == -1)
+       {
+               PartitionScheme part_scheme = joinrel->part_scheme;
+               PartitionBoundInfo boundinfo = NULL;
+               int                     nparts = 0;
+
+               Assert(joinrel->boundinfo == NULL);
+               Assert(joinrel->part_rels == NULL);
+
+               /*
+                * See if the partition bounds for inputs are exactly the same, in
+                * which case we don't need to work hard: the join rel have the same
+                * partition bounds as inputs, and the partitions with the same
+                * cardinal positions form the pairs.
+                *
+                * Note: even in cases where one or both inputs have merged bounds, it
+                * would be possible for both the bounds to be exactly the same, but
+                * it seems unlikely to be worth the cycles to check.
+                */
+               if (!rel1->partbounds_merged &&
+                       !rel2->partbounds_merged &&
+                       rel1->nparts == rel2->nparts &&
+                       partition_bounds_equal(part_scheme->partnatts,
+                                                                  part_scheme->parttyplen,
+                                                                  part_scheme->parttypbyval,
+                                                                  rel1->boundinfo, rel2->boundinfo))
+               {
+                       boundinfo = rel1->boundinfo;
+                       nparts = rel1->nparts;
+               }
+               else
+               {
+                       /* Try merging the partition bounds for inputs. */
+                       boundinfo = partition_bounds_merge(part_scheme->partnatts,
+                                                                                          part_scheme->partsupfunc,
+                                                                                          part_scheme->partcollation,
+                                                                                          rel1, rel2,
+                                                                                          parent_sjinfo->jointype,
+                                                                                          parts1, parts2);
+                       if (boundinfo == NULL)
+                       {
+                               joinrel->nparts = 0;
+                               return;
+                       }
+                       nparts = list_length(*parts1);
+                       joinrel->partbounds_merged = true;
+               }
+
+               Assert(nparts > 0);
+               joinrel->boundinfo = boundinfo;
+               joinrel->nparts = nparts;
+               joinrel->part_rels =
+                       (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
+       }
+       else
+       {
+               Assert(joinrel->nparts > 0);
+               Assert(joinrel->boundinfo);
+               Assert(joinrel->part_rels);
+
+               /*
+                * If the join rel's partbounds_merged flag is true, it means inputs
+                * are not guaranteed to have the same partition bounds, therefore we
+                * can't assume that the partitions at the same cardinal positions
+                * form the pairs; let get_matching_part_pairs() generate the pairs.
+                * Otherwise, nothing to do since we can assume that.
+                */
+               if (joinrel->partbounds_merged)
+               {
+                       get_matching_part_pairs(root, joinrel, rel1, rel2,
+                                                                       parts1, parts2);
+                       Assert(list_length(*parts1) == joinrel->nparts);
+                       Assert(list_length(*parts2) == joinrel->nparts);
+               }
+       }
+}
+
+
+/*
  * Assess whether join between given two partitioned relations can be broken
  * down into joins between matching partitions; a technique called
  * "partitionwise join"
@@ -1051,25 +1260,29 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 {
        bool            rel1_is_simple = IS_SIMPLE_REL(rel1);
        bool            rel2_is_simple = IS_SIMPLE_REL(rel2);
-       int                     nparts;
+       List       *parts1 = NIL;
+       List       *parts2 = NIL;
+       ListCell   *lcr1 = NULL;
+       ListCell   *lcr2 = NULL;
        int                     cnt_parts;
 
        /* Guard against stack overflow due to overly deep partition hierarchy. */
        check_stack_depth();
 
        /* Nothing to do, if the join relation is not partitioned. */
-       if (!IS_PARTITIONED_REL(joinrel))
+       if (joinrel->part_scheme == NULL || joinrel->nparts == 0)
                return;
 
        /* The join relation should have consider_partitionwise_join set. */
        Assert(joinrel->consider_partitionwise_join);
 
        /*
-        * Since this join relation is partitioned, all the base relations
-        * participating in this join must be partitioned and so are all the
-        * intermediate join relations.
+        * We can not perform partitionwise join if either of the joining
+        * relations is not partitioned.
         */
-       Assert(IS_PARTITIONED_REL(rel1) && IS_PARTITIONED_REL(rel2));
+       if (!IS_PARTITIONED_REL(rel1) || !IS_PARTITIONED_REL(rel2))
+               return;
+
        Assert(REL_HAS_ALL_PART_PROPS(rel1) && REL_HAS_ALL_PART_PROPS(rel2));
 
        /* The joining relations should have consider_partitionwise_join set. */
@@ -1083,35 +1296,28 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
        Assert(joinrel->part_scheme == rel1->part_scheme &&
                   joinrel->part_scheme == rel2->part_scheme);
 
-       /*
-        * Since we allow partitionwise join only when the partition bounds of the
-        * joining relations exactly match, the partition bounds of the join
-        * should match those of the joining relations.
-        */
-       Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
-                                                                 joinrel->part_scheme->parttyplen,
-                                                                 joinrel->part_scheme->parttypbyval,
-                                                                 joinrel->boundinfo, rel1->boundinfo));
-       Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
-                                                                 joinrel->part_scheme->parttyplen,
-                                                                 joinrel->part_scheme->parttypbyval,
-                                                                 joinrel->boundinfo, rel2->boundinfo));
+       Assert(!(joinrel->partbounds_merged && (joinrel->nparts <= 0)));
+
+       compute_partition_bounds(root, rel1, rel2, joinrel, parent_sjinfo,
+                                                        &parts1, &parts2);
 
-       nparts = joinrel->nparts;
+       if (joinrel->partbounds_merged)
+       {
+               lcr1 = list_head(parts1);
+               lcr2 = list_head(parts2);
+       }
 
        /*
         * Create child-join relations for this partitioned join, if those don't
         * exist. Add paths to child-joins for a pair of child relations
         * corresponding to the given pair of parent relations.
         */
-       for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+       for (cnt_parts = 0; cnt_parts < joinrel->nparts; cnt_parts++)
        {
-               RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts];
-               RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts];
-               bool            rel1_empty = (child_rel1 == NULL ||
-                                                                 IS_DUMMY_REL(child_rel1));
-               bool            rel2_empty = (child_rel2 == NULL ||
-                                                                 IS_DUMMY_REL(child_rel2));
+               RelOptInfo *child_rel1;
+               RelOptInfo *child_rel2;
+               bool            rel1_empty;
+               bool            rel2_empty;
                SpecialJoinInfo *child_sjinfo;
                List       *child_restrictlist;
                RelOptInfo *child_joinrel;
@@ -1119,6 +1325,22 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
                AppendRelInfo **appinfos;
                int                     nappinfos;
 
+               if (joinrel->partbounds_merged)
+               {
+                       child_rel1 = lfirst_node(RelOptInfo, lcr1);
+                       child_rel2 = lfirst_node(RelOptInfo, lcr2);
+                       lcr1 = lnext(parts1, lcr1);
+                       lcr2 = lnext(parts2, lcr2);
+               }
+               else
+               {
+                       child_rel1 = rel1->part_rels[cnt_parts];
+                       child_rel2 = rel2->part_rels[cnt_parts];
+               }
+
+               rel1_empty = (child_rel1 == NULL || IS_DUMMY_REL(child_rel1));
+               rel2_empty = (child_rel2 == NULL || IS_DUMMY_REL(child_rel2));
+
                /*
                 * Check for cases where we can prove that this segment of the join
                 * returns no rows, due to one or both inputs being empty (including
@@ -1216,6 +1438,8 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
                                                                                                 child_sjinfo,
                                                                                                 child_sjinfo->jointype);
                        joinrel->part_rels[cnt_parts] = child_joinrel;
+                       joinrel->all_partrels = bms_add_members(joinrel->all_partrels,
+                                                                                                       child_joinrel->relids);
                }
 
                Assert(bms_equal(child_joinrel->relids, child_joinrelids));