@ -51,9 +51,12 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
JoinType jointype ,
JoinType jointype ,
Relids qualscope ,
Relids qualscope ,
Relids ojscope ,
Relids ojscope ,
Relids outerjoin_nonnullable ) ;
Relids outerjoin_nonnullable ,
Relids deduced_nullable_relids ) ;
static bool check_outerjoin_delay ( PlannerInfo * root , Relids * relids_p ,
static bool check_outerjoin_delay ( PlannerInfo * root , Relids * relids_p ,
Relids * nullable_relids_p , bool is_pushed_down ) ;
Relids * nullable_relids_p , bool is_pushed_down ) ;
static bool check_equivalence_delay ( PlannerInfo * root ,
RestrictInfo * restrictinfo ) ;
static bool check_redundant_nullability_qual ( PlannerInfo * root , Node * clause ) ;
static bool check_redundant_nullability_qual ( PlannerInfo * root , Node * clause ) ;
static void check_mergejoinable ( RestrictInfo * restrictinfo ) ;
static void check_mergejoinable ( RestrictInfo * restrictinfo ) ;
static void check_hashjoinable ( RestrictInfo * restrictinfo ) ;
static void check_hashjoinable ( RestrictInfo * restrictinfo ) ;
@ -641,7 +644,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
distribute_qual_to_rels ( root , qual ,
distribute_qual_to_rels ( root , qual ,
false , below_outer_join , JOIN_INNER ,
false , below_outer_join , JOIN_INNER ,
* qualscope , NULL , NULL ) ;
* qualscope , NULL , NULL , NULL ) ;
}
}
}
}
else if ( IsA ( jtnode , JoinExpr ) )
else if ( IsA ( jtnode , JoinExpr ) )
@ -765,7 +768,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
distribute_qual_to_rels ( root , qual ,
distribute_qual_to_rels ( root , qual ,
false , below_outer_join , j - > jointype ,
false , below_outer_join , j - > jointype ,
* qualscope ,
* qualscope ,
ojscope , nonnullable_rels ) ;
ojscope , nonnullable_rels , NULL ) ;
}
}
/* Now we can add the SpecialJoinInfo to join_info_list */
/* Now we can add the SpecialJoinInfo to join_info_list */
@ -1074,13 +1077,19 @@ make_outerjoininfo(PlannerInfo *root,
* baserels appearing on the outer ( nonnullable ) side of the join
* baserels appearing on the outer ( nonnullable ) side of the join
* ( for FULL JOIN this includes both sides of the join , and must in fact
* ( for FULL JOIN this includes both sides of the join , and must in fact
* equal qualscope )
* equal qualscope )
* ' deduced_nullable_relids ' : if is_deduced is TRUE , the nullable relids to
* impute to the clause ; otherwise NULL
*
*
* ' qualscope ' identifies what level of JOIN the qual came from syntactically .
* ' qualscope ' identifies what level of JOIN the qual came from syntactically .
* ' ojscope ' is needed if we decide to force the qual up to the outer - join
* ' ojscope ' is needed if we decide to force the qual up to the outer - join
* level , which will be ojscope not necessarily qualscope .
* level , which will be ojscope not necessarily qualscope .
*
*
* At the time this is called , root - > join_info_list must contain entries for
* In normal use ( when is_deduced is FALSE ) , at the time this is called ,
* all and only those special joins that are syntactically below this qual .
* root - > join_info_list must contain entries for all and only those special
* joins that are syntactically below this qual . But when is_deduced is TRUE ,
* we are adding new deduced clauses after completion of deconstruct_jointree ,
* so it cannot be assumed that root - > join_info_list has anything to do with
* qual placement .
*/
*/
static void
static void
distribute_qual_to_rels ( PlannerInfo * root , Node * clause ,
distribute_qual_to_rels ( PlannerInfo * root , Node * clause ,
@ -1089,7 +1098,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
JoinType jointype ,
JoinType jointype ,
Relids qualscope ,
Relids qualscope ,
Relids ojscope ,
Relids ojscope ,
Relids outerjoin_nonnullable )
Relids outerjoin_nonnullable ,
Relids deduced_nullable_relids )
{
{
Relids relids ;
Relids relids ;
bool is_pushed_down ;
bool is_pushed_down ;
@ -1211,12 +1221,13 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* If the qual came from implied - equality deduction , it should not be
* If the qual came from implied - equality deduction , it should not be
* outerjoin - delayed , else deducer blew it . But we can ' t check this
* outerjoin - delayed , else deducer blew it . But we can ' t check this
* because the join_info_list may now contain OJs above where the qual
* because the join_info_list may now contain OJs above where the qual
* belongs .
* belongs . For the same reason , we must rely on caller to supply the
* correct nullable_relids set .
*/
*/
Assert ( ! ojscope ) ;
Assert ( ! ojscope ) ;
is_pushed_down = true ;
is_pushed_down = true ;
outerjoin_delayed = false ;
outerjoin_delayed = false ;
nullable_relids = NULL ;
nullable_relids = deduced_nullable_relids ;
/* Don't feed it back for more deductions */
/* Don't feed it back for more deductions */
maybe_equivalence = false ;
maybe_equivalence = false ;
maybe_outer_join = false ;
maybe_outer_join = false ;
@ -1388,7 +1399,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
{
{
if ( maybe_equivalence )
if ( maybe_equivalence )
{
{
if ( process_equivalence ( root , restrictinfo , below_outer_join ) )
if ( check_equivalence_delay ( root , restrictinfo ) & &
process_equivalence ( root , restrictinfo , below_outer_join ) )
return ;
return ;
/* EC rejected it, so set left_ec/right_ec the hard way ... */
/* EC rejected it, so set left_ec/right_ec the hard way ... */
initialize_mergeclause_eclasses ( root , restrictinfo ) ;
initialize_mergeclause_eclasses ( root , restrictinfo ) ;
@ -1560,6 +1572,44 @@ check_outerjoin_delay(PlannerInfo *root,
return outerjoin_delayed ;
return outerjoin_delayed ;
}
}
/*
* check_equivalence_delay
* Detect whether a potential equivalence clause is rendered unsafe
* by outer - join - delay considerations . Return TRUE if it ' s safe .
*
* The initial tests in distribute_qual_to_rels will consider a mergejoinable
* clause to be a potential equivalence clause if it is not outerjoin_delayed .
* But since the point of equivalence processing is that we will recombine the
* two sides of the clause with others , we have to check that each side
* satisfies the not - outerjoin_delayed condition on its own ; otherwise it might
* not be safe to evaluate everywhere we could place a derived equivalence
* condition .
*/
static bool
check_equivalence_delay ( PlannerInfo * root ,
RestrictInfo * restrictinfo )
{
Relids relids ;
Relids nullable_relids ;
/* fast path if no special joins */
if ( root - > join_info_list = = NIL )
return true ;
/* must copy restrictinfo's relids to avoid changing it */
relids = bms_copy ( restrictinfo - > left_relids ) ;
/* check left side does not need delay */
if ( check_outerjoin_delay ( root , & relids , & nullable_relids , true ) )
return false ;
/* and similarly for the right side */
relids = bms_copy ( restrictinfo - > right_relids ) ;
if ( check_outerjoin_delay ( root , & relids , & nullable_relids , true ) )
return false ;
return true ;
}
/*
/*
* check_redundant_nullability_qual
* check_redundant_nullability_qual
* Check to see if the qual is an IS NULL qual that is redundant with
* Check to see if the qual is an IS NULL qual that is redundant with
@ -1670,11 +1720,20 @@ distribute_restrictinfo_to_rels(PlannerInfo *root,
* variable - free . Otherwise the qual is applied at the lowest join level
* variable - free . Otherwise the qual is applied at the lowest join level
* that provides all its variables .
* that provides all its variables .
*
*
* " nullable_relids " is the set of relids used in the expressions that are
* potentially nullable below the expressions . ( This has to be supplied by
* caller because this function is used after deconstruct_jointree , so we
* don ' t have knowledge of where the clause items came from . )
*
* " both_const " indicates whether both items are known pseudo - constant ;
* " both_const " indicates whether both items are known pseudo - constant ;
* in this case it is worth applying eval_const_expressions ( ) in case we
* in this case it is worth applying eval_const_expressions ( ) in case we
* can produce constant TRUE or constant FALSE . ( Otherwise it ' s not ,
* can produce constant TRUE or constant FALSE . ( Otherwise it ' s not ,
* because the expressions went through eval_const_expressions already . )
* because the expressions went through eval_const_expressions already . )
*
*
* Note : this function will copy item1 and item2 , but it is caller ' s
* responsibility to make sure that the Relids parameters are fresh copies
* not shared with other uses .
*
* This is currently used only when an EquivalenceClass is found to
* This is currently used only when an EquivalenceClass is found to
* contain pseudoconstants . See path / pathkeys . c for more details .
* contain pseudoconstants . See path / pathkeys . c for more details .
*/
*/
@ -1685,6 +1744,7 @@ process_implied_equality(PlannerInfo *root,
Expr * item1 ,
Expr * item1 ,
Expr * item2 ,
Expr * item2 ,
Relids qualscope ,
Relids qualscope ,
Relids nullable_relids ,
bool below_outer_join ,
bool below_outer_join ,
bool both_const )
bool both_const )
{
{
@ -1718,15 +1778,12 @@ process_implied_equality(PlannerInfo *root,
}
}
}
}
/* Make a copy of qualscope to avoid problems if source EC changes */
qualscope = bms_copy ( qualscope ) ;
/*
/*
* Push the new clause into all the appropriate restrictinfo lists .
* Push the new clause into all the appropriate restrictinfo lists .
*/
*/
distribute_qual_to_rels ( root , ( Node * ) clause ,
distribute_qual_to_rels ( root , ( Node * ) clause ,
true , below_outer_join , JOIN_INNER ,
true , below_outer_join , JOIN_INNER ,
qualscope , NULL , NULL ) ;
qualscope , NULL , NULL , nullable_relids ) ;
}
}
/*
/*
@ -1735,6 +1792,10 @@ process_implied_equality(PlannerInfo *root,
* This overlaps the functionality of process_implied_equality ( ) , but we
* This overlaps the functionality of process_implied_equality ( ) , but we
* must return the RestrictInfo , not push it into the joininfo tree .
* must return the RestrictInfo , not push it into the joininfo tree .
*
*
* Note : this function will copy item1 and item2 , but it is caller ' s
* responsibility to make sure that the Relids parameters are fresh copies
* not shared with other uses .
*
* Note : we do not do initialize_mergeclause_eclasses ( ) here . It is
* Note : we do not do initialize_mergeclause_eclasses ( ) here . It is
* caller ' s responsibility that left_ec / right_ec be set as necessary .
* caller ' s responsibility that left_ec / right_ec be set as necessary .
*/
*/
@ -1743,7 +1804,8 @@ build_implied_join_equality(Oid opno,
Oid collation ,
Oid collation ,
Expr * item1 ,
Expr * item1 ,
Expr * item2 ,
Expr * item2 ,
Relids qualscope )
Relids qualscope ,
Relids nullable_relids )
{
{
RestrictInfo * restrictinfo ;
RestrictInfo * restrictinfo ;
Expr * clause ;
Expr * clause ;
@ -1760,9 +1822,6 @@ build_implied_join_equality(Oid opno,
InvalidOid ,
InvalidOid ,
collation ) ;
collation ) ;
/* Make a copy of qualscope to avoid problems if source EC changes */
qualscope = bms_copy ( qualscope ) ;
/*
/*
* Build the RestrictInfo node itself .
* Build the RestrictInfo node itself .
*/
*/
@ -1772,7 +1831,7 @@ build_implied_join_equality(Oid opno,
false , /* pseudoconstant */
false , /* pseudoconstant */
qualscope , /* required_relids */
qualscope , /* required_relids */
NULL , /* outer_relids */
NULL , /* outer_relids */
NULL ) ; /* nullable_relids */
nullable_relids ) ; /* nullable_relids */
/* Set mergejoinability/hashjoinability flags */
/* Set mergejoinability/hashjoinability flags */
check_mergejoinable ( restrictinfo ) ;
check_mergejoinable ( restrictinfo ) ;