Re: Revert: Remove useless self-joins *and* -DREALLOCATE_BITMAPSETS make server crash, regress test fail.

Поиск
Список
Период
Сортировка
I traced down the other failure I was seeing in check-world, and
found that it came from deconstruct_distribute doing this:

        distribute_quals_to_rels(root, my_quals,
                                 jtitem,
                                 sjinfo,
                                 root->qual_security_level,
                                 jtitem->qualscope,
                                 ojscope, jtitem->nonnullable_rels,
                                 NULL,    /* incompatible_relids */
                                 true,    /* allow_equivalence */
                                 false, false,    /* not clones */
                                 postponed_oj_qual_list);

where jtitem->nonnullable_rels is the same as the jtitem's left_rels,
which ends up as the syn_lefthand of the join's SpecialJoinInfo, and
then when remove_rel_from_query tries to adjust the syn_lefthand it
breaks the outer_relids of whatever RestrictInfos got made here.

I was able to fix that by not letting jtitem->nonnullable_rels be
the same as left_rels.  The attached alternative_1.patch does pass
check-world.  But I find it mighty unprincipled: the JoinTreeItem
data structures are full of shared relid sets, so why is this
particular sharing not OK?  I still don't have any confidence that
there aren't more problems.

Along about here I started to wonder how come we are only seeing
SpecialJoinInfo-vs-RestrictInfo sharing as a problem, when surely
there is plenty of cross-RestrictInfo sharing going on as well.
(The above call is perfectly capable of making a lot of RestrictInfos,
all with the same outer_relids.)  That thought led me to look at
remove_rel_from_restrictinfo, and darn if I didn't find this:

    /*
     * The clause_relids probably aren't shared with anything else, but let's
     * copy them just to be sure.
     */
    rinfo->clause_relids = bms_copy(rinfo->clause_relids);
    ...
    /* Likewise for required_relids */
    rinfo->required_relids = bms_copy(rinfo->required_relids);

So the reason we don't see cross-RestrictInfo breakage is that
analyzejoins.c is careful not to modify the original relid sets
when modifying a RestrictInfo.  (This comment is clearly wrong.)

And that leads to the thought that we can fix our current sharing
problems by similarly avoiding overwriting the original sets
in remove_rel_from_query.  The attached alternative-2.patch
attacks it that way, and also passes check-world.

I like alternative-2.patch a lot better, not least because it
only adds cycles when join removal actually fires.  Basically
this is putting the onus on the data structure modifier to
cope with shared bitmapsets, rather than trying to say that
sharing is disallowed.

Thoughts?

            regards, tom lane

diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index e2c68fe6f9..4198e9c83a 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -979,7 +979,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
                                                     right_item->inner_join_rels);
                 jtitem->left_rels = left_item->qualscope;
                 jtitem->right_rels = right_item->qualscope;
-                jtitem->nonnullable_rels = left_item->qualscope;
+                jtitem->nonnullable_rels = bms_copy(left_item->qualscope);
                 break;
             case JOIN_SEMI:
                 /* This node belongs to parent_domain, as do its children */
@@ -1053,7 +1053,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
                 jtitem->left_rels = left_item->qualscope;
                 jtitem->right_rels = right_item->qualscope;
                 /* each side is both outer and inner */
-                jtitem->nonnullable_rels = jtitem->qualscope;
+                jtitem->nonnullable_rels = bms_copy(jtitem->qualscope);
                 break;
             default:
                 /* JOIN_RIGHT was eliminated during reduce_outer_joins() */
@@ -1888,7 +1888,7 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
     qualscope = bms_union(sjinfo->syn_lefthand, sjinfo->syn_righthand);
     qualscope = bms_add_member(qualscope, sjinfo->ojrelid);
     ojscope = bms_union(sjinfo->min_lefthand, sjinfo->min_righthand);
-    nonnullable_rels = sjinfo->syn_lefthand;
+    nonnullable_rels = bms_copy(sjinfo->syn_lefthand);

     /*
      * If this join can commute with any other ones per outer-join identity 3,
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index aa72592567..1c9acf864c 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -390,6 +390,17 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
     {
         SpecialJoinInfo *sjinf = (SpecialJoinInfo *) lfirst(l);

+        /*
+         * initsplan.c is fairly cavalier about allowing SpecialJoinInfos'
+         * lefthand/righthand relid sets to be shared with other data
+         * structures.  Ensure that we don't modify the original relid sets.
+         * (The commute_xxx sets are always per-SpecialJoinInfo though.)
+         */
+        sjinf->min_lefthand = bms_copy(sjinf->min_lefthand);
+        sjinf->min_righthand = bms_copy(sjinf->min_righthand);
+        sjinf->syn_lefthand = bms_copy(sjinf->syn_lefthand);
+        sjinf->syn_righthand = bms_copy(sjinf->syn_righthand);
+        /* Now remove relid and ojrelid bits from the sets: */
         sjinf->min_lefthand = bms_del_member(sjinf->min_lefthand, relid);
         sjinf->min_righthand = bms_del_member(sjinf->min_righthand, relid);
         sjinf->syn_lefthand = bms_del_member(sjinf->syn_lefthand, relid);
@@ -551,8 +562,10 @@ static void
 remove_rel_from_restrictinfo(RestrictInfo *rinfo, int relid, int ojrelid)
 {
     /*
-     * The clause_relids probably aren't shared with anything else, but let's
-     * copy them just to be sure.
+     * initsplan.c is fairly cavalier about allowing RestrictInfos to share
+     * relid sets with other RestrictInfos (and SpecialJoinInfos for that
+     * matter).  To avoid breaking things, make sure this RestrictInfo has its
+     * own clause_relids set before we modify it.
      */
     rinfo->clause_relids = bms_copy(rinfo->clause_relids);
     rinfo->clause_relids = bms_del_member(rinfo->clause_relids, relid);

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Alexander Korotkov
Дата:
Сообщение: Re: Add SPLIT PARTITION/MERGE PARTITIONS commands
Следующее
От: Nathan Bossart
Дата:
Сообщение: Re: add --no-sync to pg_upgrade's calls to pg_dump and pg_dumpall