Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit72a4231

Browse files
committed
Fix planning of non-strict equivalence clauses above outer joins.
If a potential equivalence clause references a variable from the nullableside of an outer join, the planner needs to take care that derived clausesare not pushed to below the outer join; else they may use the wrong valuefor the variable. (The problem arises only with non-strict clauses, sinceif an upper clause can be proven strict then the outer join will getsimplified to a plain join.) The planner attempted to prevent this typeof error by checking that potential equivalence clauses aren'touterjoin-delayed as a whole, but actually we have to check each sideseparately, since the two sides of the clause will get moved aroundseparately if it's treated as an equivalence. Bugs of this type can bedemonstrated as far back as 7.4, even though releases before 8.3 had onlya very ad-hoc notion of equivalence clauses.In addition, we neglected to account for the possibility that such clausesmight have nonempty nullable_relids even when not outerjoin-delayed; so theequivalence-class machinery lacked logic to compute correct nullable_relidsvalues for clauses it constructs. This oversight was harmless before 9.2because we were only using RestrictInfo.nullable_relids for OR clauses;but as of 9.2 it could result in pushing constructed equivalence clausesto incorrect places. (This accounts for bug #7604 from Bill MacArthur.)Fix the first problem by adding a new test check_equivalence_delay() indistribute_qual_to_rels, and fix the second one by adding code inequivclass.c and called functions to set correct nullable_relids forgenerated clauses. Although I believe the second part of this is notcurrently necessary before 9.2, I chose to back-patch it anyway, partly tokeep the logic similar across branches and partly because it seems possiblewe might find other reasons why we need valid values of nullable_relids inthe older branches.Add regression tests illustrating these problems. In 9.0 and up, alsoadd test cases checking that we can push constants through outer joins,since we've broken that optimization before and I nearly broke it againwith an overly simplistic patch for this problem.
1 parent7b583b2 commit72a4231

File tree

7 files changed

+248
-36
lines changed

7 files changed

+248
-36
lines changed

‎src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,7 @@ _outEquivalenceMember(StringInfo str, const EquivalenceMember *node)
18151815

18161816
WRITE_NODE_FIELD(em_expr);
18171817
WRITE_BITMAPSET_FIELD(em_relids);
1818+
WRITE_BITMAPSET_FIELD(em_nullable_relids);
18181819
WRITE_BOOL_FIELD(em_is_const);
18191820
WRITE_BOOL_FIELD(em_is_child);
18201821
WRITE_OID_FIELD(em_datatype);

‎src/backend/optimizer/path/equivclass.c

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131

3232
staticEquivalenceMember*add_eq_member(EquivalenceClass*ec,
33-
Expr*expr,Relidsrelids,
33+
Expr*expr,Relidsrelids,Relidsnullable_relids,
3434
boolis_child,Oiddatatype);
3535
staticvoidgenerate_base_implied_equalities_const(PlannerInfo*root,
3636
EquivalenceClass*ec);
@@ -106,7 +106,9 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
106106
Expr*item1;
107107
Expr*item2;
108108
Relidsitem1_relids,
109-
item2_relids;
109+
item2_relids,
110+
item1_nullable_relids,
111+
item2_nullable_relids;
110112
List*opfamilies;
111113
EquivalenceClass*ec1,
112114
*ec2;
@@ -163,6 +165,12 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
163165
return false;/* RHS is non-strict but not constant */
164166
}
165167

168+
/* Calculate nullable-relid sets for each side of the clause */
169+
item1_nullable_relids=bms_intersect(item1_relids,
170+
restrictinfo->nullable_relids);
171+
item2_nullable_relids=bms_intersect(item2_relids,
172+
restrictinfo->nullable_relids);
173+
166174
/*
167175
* We use the declared input types of the operator, not exprType() of the
168176
* inputs, as the nominal datatypes for opfamily lookup. This presumes
@@ -309,7 +317,8 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
309317
elseif (ec1)
310318
{
311319
/* Case 3: add item2 to ec1 */
312-
em2=add_eq_member(ec1,item2,item2_relids, false,item2_type);
320+
em2=add_eq_member(ec1,item2,item2_relids,item2_nullable_relids,
321+
false,item2_type);
313322
ec1->ec_sources=lappend(ec1->ec_sources,restrictinfo);
314323
ec1->ec_below_outer_join |=below_outer_join;
315324
/* mark the RI as associated with this eclass */
@@ -322,7 +331,8 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
322331
elseif (ec2)
323332
{
324333
/* Case 3: add item1 to ec2 */
325-
em1=add_eq_member(ec2,item1,item1_relids, false,item1_type);
334+
em1=add_eq_member(ec2,item1,item1_relids,item1_nullable_relids,
335+
false,item1_type);
326336
ec2->ec_sources=lappend(ec2->ec_sources,restrictinfo);
327337
ec2->ec_below_outer_join |=below_outer_join;
328338
/* mark the RI as associated with this eclass */
@@ -349,8 +359,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
349359
ec->ec_broken= false;
350360
ec->ec_sortref=0;
351361
ec->ec_merged=NULL;
352-
em1=add_eq_member(ec,item1,item1_relids, false,item1_type);
353-
em2=add_eq_member(ec,item2,item2_relids, false,item2_type);
362+
em1=add_eq_member(ec,item1,item1_relids,item1_nullable_relids,
363+
false,item1_type);
364+
em2=add_eq_member(ec,item2,item2_relids,item2_nullable_relids,
365+
false,item2_type);
354366

355367
root->eq_classes=lappend(root->eq_classes,ec);
356368

@@ -448,12 +460,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
448460
*/
449461
staticEquivalenceMember*
450462
add_eq_member(EquivalenceClass*ec,Expr*expr,Relidsrelids,
451-
boolis_child,Oiddatatype)
463+
Relidsnullable_relids,boolis_child,Oiddatatype)
452464
{
453465
EquivalenceMember*em=makeNode(EquivalenceMember);
454466

455467
em->em_expr=expr;
456468
em->em_relids=relids;
469+
em->em_nullable_relids=nullable_relids;
457470
em->em_is_const= false;
458471
em->em_is_child=is_child;
459472
em->em_datatype=datatype;
@@ -609,7 +622,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
609622
elog(ERROR,"volatile EquivalenceClass has no sortref");
610623

611624
newem=add_eq_member(newec,copyObject(expr),pull_varnos((Node*)expr),
612-
false,opcintype);
625+
NULL,false,opcintype);
613626

614627
/*
615628
* add_eq_member doesn't check for volatile functions, set-returning
@@ -789,7 +802,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
789802
}
790803
process_implied_equality(root,eq_op,ec->ec_collation,
791804
cur_em->em_expr,const_em->em_expr,
792-
ec->ec_relids,
805+
bms_copy(ec->ec_relids),
806+
bms_union(cur_em->em_nullable_relids,
807+
const_em->em_nullable_relids),
793808
ec->ec_below_outer_join,
794809
cur_em->em_is_const);
795810
}
@@ -844,7 +859,9 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
844859
}
845860
process_implied_equality(root,eq_op,ec->ec_collation,
846861
prev_em->em_expr,cur_em->em_expr,
847-
ec->ec_relids,
862+
bms_copy(ec->ec_relids),
863+
bms_union(prev_em->em_nullable_relids,
864+
cur_em->em_nullable_relids),
848865
ec->ec_below_outer_join,
849866
false);
850867
}
@@ -1312,7 +1329,9 @@ create_join_clause(PlannerInfo *root,
13121329
leftem->em_expr,
13131330
rightem->em_expr,
13141331
bms_union(leftem->em_relids,
1315-
rightem->em_relids));
1332+
rightem->em_relids),
1333+
bms_union(leftem->em_nullable_relids,
1334+
rightem->em_nullable_relids));
13161335

13171336
/* Mark the clause as redundant, or not */
13181337
rinfo->parent_ec=parent_ec;
@@ -1534,7 +1553,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
15341553
left_type,
15351554
right_type,
15361555
inner_datatype;
1537-
Relidsinner_relids;
1556+
Relidsinner_relids,
1557+
inner_nullable_relids;
15381558
ListCell*lc1;
15391559

15401560
Assert(is_opclause(rinfo->clause));
@@ -1561,6 +1581,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
15611581
inner_datatype=left_type;
15621582
inner_relids=rinfo->left_relids;
15631583
}
1584+
inner_nullable_relids=bms_intersect(inner_relids,
1585+
rinfo->nullable_relids);
15641586

15651587
/* Scan EquivalenceClasses for a match to outervar */
15661588
foreach(lc1,root->eq_classes)
@@ -1619,7 +1641,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
16191641
cur_ec->ec_collation,
16201642
innervar,
16211643
cur_em->em_expr,
1622-
inner_relids);
1644+
bms_copy(inner_relids),
1645+
bms_copy(inner_nullable_relids));
16231646
if (process_equivalence(root,newrinfo, true))
16241647
match= true;
16251648
}
@@ -1653,7 +1676,9 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
16531676
left_type,
16541677
right_type;
16551678
Relidsleft_relids,
1656-
right_relids;
1679+
right_relids,
1680+
left_nullable_relids,
1681+
right_nullable_relids;
16571682
ListCell*lc1;
16581683

16591684
/* Can't use an outerjoin_delayed clause here */
@@ -1669,6 +1694,10 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
16691694
rightvar= (Expr*)get_rightop(rinfo->clause);
16701695
left_relids=rinfo->left_relids;
16711696
right_relids=rinfo->right_relids;
1697+
left_nullable_relids=bms_intersect(left_relids,
1698+
rinfo->nullable_relids);
1699+
right_nullable_relids=bms_intersect(right_relids,
1700+
rinfo->nullable_relids);
16721701

16731702
foreach(lc1,root->eq_classes)
16741703
{
@@ -1754,7 +1783,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
17541783
cur_ec->ec_collation,
17551784
leftvar,
17561785
cur_em->em_expr,
1757-
left_relids);
1786+
bms_copy(left_relids),
1787+
bms_copy(left_nullable_relids));
17581788
if (process_equivalence(root,newrinfo, true))
17591789
matchleft= true;
17601790
}
@@ -1767,7 +1797,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
17671797
cur_ec->ec_collation,
17681798
rightvar,
17691799
cur_em->em_expr,
1770-
right_relids);
1800+
bms_copy(right_relids),
1801+
bms_copy(right_nullable_relids));
17711802
if (process_equivalence(root,newrinfo, true))
17721803
matchright= true;
17731804
}
@@ -1894,6 +1925,7 @@ add_child_rel_equivalences(PlannerInfo *root,
18941925
/* Yes, generate transformed child version */
18951926
Expr*child_expr;
18961927
Relidsnew_relids;
1928+
Relidsnew_nullable_relids;
18971929

18981930
child_expr= (Expr*)
18991931
adjust_appendrel_attrs(root,
@@ -1910,7 +1942,21 @@ add_child_rel_equivalences(PlannerInfo *root,
19101942
parent_rel->relids);
19111943
new_relids=bms_add_members(new_relids,child_rel->relids);
19121944

1913-
(void)add_eq_member(cur_ec,child_expr,new_relids,
1945+
/*
1946+
* And likewise for nullable_relids. Note this code assumes
1947+
* parent and child relids are singletons.
1948+
*/
1949+
new_nullable_relids=cur_em->em_nullable_relids;
1950+
if (bms_overlap(new_nullable_relids,parent_rel->relids))
1951+
{
1952+
new_nullable_relids=bms_difference(new_nullable_relids,
1953+
parent_rel->relids);
1954+
new_nullable_relids=bms_add_members(new_nullable_relids,
1955+
child_rel->relids);
1956+
}
1957+
1958+
(void)add_eq_member(cur_ec,child_expr,
1959+
new_relids,new_nullable_relids,
19141960
true,cur_em->em_datatype);
19151961
}
19161962
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp