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

Commitafdc751

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 parentdf35b7e commitafdc751

File tree

7 files changed

+252
-36
lines changed

7 files changed

+252
-36
lines changed

‎src/backend/nodes/outfuncs.c

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

16531653
WRITE_NODE_FIELD(em_expr);
16541654
WRITE_BITMAPSET_FIELD(em_relids);
1655+
WRITE_BITMAPSET_FIELD(em_nullable_relids);
16551656
WRITE_BOOL_FIELD(em_is_const);
16561657
WRITE_BOOL_FIELD(em_is_child);
16571658
WRITE_OID_FIELD(em_datatype);

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

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929

3030
staticEquivalenceMember*add_eq_member(EquivalenceClass*ec,
31-
Expr*expr,Relidsrelids,
31+
Expr*expr,Relidsrelids,Relidsnullable_relids,
3232
boolis_child,Oiddatatype);
3333
staticvoidgenerate_base_implied_equalities_const(PlannerInfo*root,
3434
EquivalenceClass*ec);
@@ -98,7 +98,9 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
9898
Expr*item1;
9999
Expr*item2;
100100
Relidsitem1_relids,
101-
item2_relids;
101+
item2_relids,
102+
item1_nullable_relids,
103+
item2_nullable_relids;
102104
List*opfamilies;
103105
EquivalenceClass*ec1,
104106
*ec2;
@@ -139,6 +141,12 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
139141
return false;/* RHS is non-strict but not constant */
140142
}
141143

144+
/* Calculate nullable-relid sets for each side of the clause */
145+
item1_nullable_relids=bms_intersect(item1_relids,
146+
restrictinfo->nullable_relids);
147+
item2_nullable_relids=bms_intersect(item2_relids,
148+
restrictinfo->nullable_relids);
149+
142150
/*
143151
* We use the declared input types of the operator, not exprType() of the
144152
* inputs, as the nominal datatypes for opfamily lookup. This presumes
@@ -273,7 +281,8 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
273281
elseif (ec1)
274282
{
275283
/* Case 3: add item2 to ec1 */
276-
em2=add_eq_member(ec1,item2,item2_relids, false,item2_type);
284+
em2=add_eq_member(ec1,item2,item2_relids,item2_nullable_relids,
285+
false,item2_type);
277286
ec1->ec_sources=lappend(ec1->ec_sources,restrictinfo);
278287
ec1->ec_below_outer_join |=below_outer_join;
279288
/* mark the RI as usable with this pair of EMs */
@@ -283,7 +292,8 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
283292
elseif (ec2)
284293
{
285294
/* Case 3: add item1 to ec2 */
286-
em1=add_eq_member(ec2,item1,item1_relids, false,item1_type);
295+
em1=add_eq_member(ec2,item1,item1_relids,item1_nullable_relids,
296+
false,item1_type);
287297
ec2->ec_sources=lappend(ec2->ec_sources,restrictinfo);
288298
ec2->ec_below_outer_join |=below_outer_join;
289299
/* mark the RI as usable with this pair of EMs */
@@ -306,8 +316,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
306316
ec->ec_broken= false;
307317
ec->ec_sortref=0;
308318
ec->ec_merged=NULL;
309-
em1=add_eq_member(ec,item1,item1_relids, false,item1_type);
310-
em2=add_eq_member(ec,item2,item2_relids, false,item2_type);
319+
em1=add_eq_member(ec,item1,item1_relids,item1_nullable_relids,
320+
false,item1_type);
321+
em2=add_eq_member(ec,item2,item2_relids,item2_nullable_relids,
322+
false,item2_type);
311323

312324
root->eq_classes=lappend(root->eq_classes,ec);
313325

@@ -324,12 +336,13 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
324336
*/
325337
staticEquivalenceMember*
326338
add_eq_member(EquivalenceClass*ec,Expr*expr,Relidsrelids,
327-
boolis_child,Oiddatatype)
339+
Relidsnullable_relids,boolis_child,Oiddatatype)
328340
{
329341
EquivalenceMember*em=makeNode(EquivalenceMember);
330342

331343
em->em_expr=expr;
332344
em->em_relids=relids;
345+
em->em_nullable_relids=nullable_relids;
333346
em->em_is_const= false;
334347
em->em_is_child=is_child;
335348
em->em_datatype=datatype;
@@ -451,7 +464,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
451464
elog(ERROR,"volatile EquivalenceClass has no sortref");
452465

453466
newem=add_eq_member(newec,expr,pull_varnos((Node*)expr),
454-
false,expr_datatype);
467+
NULL,false,expr_datatype);
455468

456469
/*
457470
* add_eq_member doesn't check for volatile functions, set-returning
@@ -631,7 +644,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
631644
}
632645
process_implied_equality(root,eq_op,
633646
cur_em->em_expr,const_em->em_expr,
634-
ec->ec_relids,
647+
bms_copy(ec->ec_relids),
648+
bms_union(cur_em->em_nullable_relids,
649+
const_em->em_nullable_relids),
635650
ec->ec_below_outer_join,
636651
cur_em->em_is_const);
637652
}
@@ -686,7 +701,9 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
686701
}
687702
process_implied_equality(root,eq_op,
688703
prev_em->em_expr,cur_em->em_expr,
689-
ec->ec_relids,
704+
bms_copy(ec->ec_relids),
705+
bms_union(prev_em->em_nullable_relids,
706+
cur_em->em_nullable_relids),
690707
ec->ec_below_outer_join,
691708
false);
692709
}
@@ -1088,7 +1105,9 @@ create_join_clause(PlannerInfo *root,
10881105
leftem->em_expr,
10891106
rightem->em_expr,
10901107
bms_union(leftem->em_relids,
1091-
rightem->em_relids));
1108+
rightem->em_relids),
1109+
bms_union(leftem->em_nullable_relids,
1110+
rightem->em_nullable_relids));
10921111

10931112
/* Mark the clause as redundant, or not */
10941113
rinfo->parent_ec=parent_ec;
@@ -1309,7 +1328,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
13091328
left_type,
13101329
right_type,
13111330
inner_datatype;
1312-
Relidsinner_relids;
1331+
Relidsinner_relids,
1332+
inner_nullable_relids;
13131333
ListCell*lc1;
13141334

13151335
Assert(is_opclause(rinfo->clause));
@@ -1335,6 +1355,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
13351355
inner_datatype=left_type;
13361356
inner_relids=rinfo->left_relids;
13371357
}
1358+
inner_nullable_relids=bms_intersect(inner_relids,
1359+
rinfo->nullable_relids);
13381360

13391361
/* Scan EquivalenceClasses for a match to outervar */
13401362
foreach(lc1,root->eq_classes)
@@ -1389,7 +1411,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
13891411
newrinfo=build_implied_join_equality(eq_op,
13901412
innervar,
13911413
cur_em->em_expr,
1392-
inner_relids);
1414+
bms_copy(inner_relids),
1415+
bms_copy(inner_nullable_relids));
13931416
if (process_equivalence(root,newrinfo, true))
13941417
match= true;
13951418
}
@@ -1422,7 +1445,9 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
14221445
left_type,
14231446
right_type;
14241447
Relidsleft_relids,
1425-
right_relids;
1448+
right_relids,
1449+
left_nullable_relids,
1450+
right_nullable_relids;
14261451
ListCell*lc1;
14271452

14281453
/* Can't use an outerjoin_delayed clause here */
@@ -1437,6 +1462,10 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
14371462
rightvar= (Expr*)get_rightop(rinfo->clause);
14381463
left_relids=rinfo->left_relids;
14391464
right_relids=rinfo->right_relids;
1465+
left_nullable_relids=bms_intersect(left_relids,
1466+
rinfo->nullable_relids);
1467+
right_nullable_relids=bms_intersect(right_relids,
1468+
rinfo->nullable_relids);
14401469

14411470
foreach(lc1,root->eq_classes)
14421471
{
@@ -1518,7 +1547,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
15181547
newrinfo=build_implied_join_equality(eq_op,
15191548
leftvar,
15201549
cur_em->em_expr,
1521-
left_relids);
1550+
bms_copy(left_relids),
1551+
bms_copy(left_nullable_relids));
15221552
if (process_equivalence(root,newrinfo, true))
15231553
matchleft= true;
15241554
}
@@ -1530,7 +1560,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
15301560
newrinfo=build_implied_join_equality(eq_op,
15311561
rightvar,
15321562
cur_em->em_expr,
1533-
right_relids);
1563+
bms_copy(right_relids),
1564+
bms_copy(right_nullable_relids));
15341565
if (process_equivalence(root,newrinfo, true))
15351566
matchright= true;
15361567
}
@@ -1650,11 +1681,27 @@ add_child_rel_equivalences(PlannerInfo *root,
16501681
{
16511682
/* Yes, generate transformed child version */
16521683
Expr*child_expr;
1684+
Relidsnew_nullable_relids;
16531685

16541686
child_expr= (Expr*)
16551687
adjust_appendrel_attrs((Node*)cur_em->em_expr,
16561688
appinfo);
1657-
(void)add_eq_member(cur_ec,child_expr,child_rel->relids,
1689+
1690+
/*
1691+
* Must translate nullable_relids. Note this code assumes
1692+
* parent and child relids are singletons.
1693+
*/
1694+
new_nullable_relids=cur_em->em_nullable_relids;
1695+
if (bms_overlap(new_nullable_relids,parent_rel->relids))
1696+
{
1697+
new_nullable_relids=bms_difference(new_nullable_relids,
1698+
parent_rel->relids);
1699+
new_nullable_relids=bms_add_members(new_nullable_relids,
1700+
child_rel->relids);
1701+
}
1702+
1703+
(void)add_eq_member(cur_ec,child_expr,
1704+
child_rel->relids,new_nullable_relids,
16581705
true,cur_em->em_datatype);
16591706
}
16601707
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp