@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / optimizer / plan / initsplan . c , v 1.127 2007 / 01 / 08 16 : 47 : 30 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / optimizer / plan / initsplan . c , v 1.128 2007 / 01 / 20 20 : 45 : 39 tgl Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
@ -37,8 +37,6 @@ int from_collapse_limit;
int join_collapse_limit ;
static void add_vars_to_targetlist ( PlannerInfo * root , List * vars ,
Relids where_needed ) ;
static List * deconstruct_recurse ( PlannerInfo * root , Node * jtnode ,
bool below_outer_join , Relids * qualscope ) ;
static OuterJoinInfo * make_outerjoininfo ( PlannerInfo * root ,
@ -51,8 +49,7 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
Relids qualscope ,
Relids ojscope ,
Relids outerjoin_nonnullable ) ;
static bool qual_is_redundant ( PlannerInfo * root , RestrictInfo * restrictinfo ,
List * restrictlist ) ;
static bool check_outerjoin_delay ( PlannerInfo * root , Relids * relids_p ) ;
static void check_mergejoinable ( RestrictInfo * restrictinfo ) ;
static void check_hashjoinable ( RestrictInfo * restrictinfo ) ;
@ -144,7 +141,7 @@ build_base_rel_tlists(PlannerInfo *root, List *final_tlist)
* as being needed for the indicated join ( or for final output if
* where_needed includes " relation 0 " ) .
*/
static void
void
add_vars_to_targetlist ( PlannerInfo * root , List * vars , Relids where_needed )
{
ListCell * temp ;
@ -590,17 +587,17 @@ make_outerjoininfo(PlannerInfo *root,
* Add clause information to either the baserestrictinfo or joininfo list
* ( depending on whether the clause is a join ) of each base relation
* mentioned in the clause . A RestrictInfo node is created and added to
* the appropriate list for each rel . Also , if the clause uses a
* the appropriate list for each rel . Alternatively , if the clause uses a
* mergejoinable operator and is not delayed by outer - join rules , enter
* the left - and right - side expressions into the query ' s lists of
* equijoined var s.
* the left - and right - side expressions into the query ' s list of
* EquivalenceClasse s.
*
* ' clause ' : the qual clause to be distributed
* ' is_pushed_down ' : if TRUE , force the clause to be marked ' is_pushed_down '
* ( this indicates the clause came from a FromExpr , not a JoinExpr )
* ' is_deduced ' : TRUE if the qual came from implied - equality deduction
* ' below_outer_join ' : TRUE if the qual is from a JOIN / ON that is below the
* nullable side of a higher - level outer join .
* nullable side of a higher - level outer join
* ' qualscope ' : set of baserels the qual ' s syntactic scope covers
* ' ojscope ' : NULL if not an outer - join qual , else the minimum set of baserels
* needed to form this join
@ -625,11 +622,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
Relids relids ;
bool outerjoin_delayed ;
bool pseudoconstant = false ;
bool maybe_equijoin ;
bool maybe_equivalence ;
bool maybe_outer_join ;
RestrictInfo * restrictinfo ;
RelOptInfo * rel ;
List * vars ;
/*
* Retrieve all relids mentioned within the clause .
@ -705,108 +700,57 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
if ( is_deduced )
{
/*
* If the qual came from implied - equality deduction , we always
* evaluate the qual at its natural semantic level . It is the
* responsibility of the deducer not to create any quals that should
* be delayed by outer - join rule s.
* If the qual came from implied - equality deduction , it should
* not be outerjoin - delayed , else deducer blew it . But we can ' t
* check this because the ojinfo list may now contain OJs above
* where the qual belong s.
*/
Assert ( bms_equal ( relids , qualscope ) ) ;
Assert ( ! ojscope ) ;
Assert ( ! pseudoconstant ) ;
/* Needn't feed it back for more deductions */
outerjoin_delayed = false ;
maybe_equijoin = false ;
/* Don't feed it back for more deductions */
maybe_equivalence = false ;
maybe_outer_join = false ;
}
else if ( bms_overlap ( relids , outerjoin_nonnullable ) )
{
/*
* The qual is attached to an outer join and mentions ( some of the )
* rels on the nonnullable side . Force the qual to be evaluated
* exactly at the level of joining corresponding to the outer join . We
* cannot let it get pushed down into the nonnullable side , since then
* we ' d produce no output rows , rather than the intended single
* null - extended row , for any nonnullable - side rows failing the qual .
* rels on the nonnullable side .
*
* Note : an outer - join qual that mentions only nullable - side rels can
* be pushed down into the nullable side without changing the join
* result , so we treat it the same as an ordinary inner - join qual ,
* except for not setting maybe_equijoin ( see below ) .
* result , so we treat it almost the same as an ordinary inner - join
* qual ( see below ) .
*
* We can ' t use such a clause to deduce equivalence ( the left and right
* sides might be unequal above the join because one of them has gone
* to NULL ) . . . but we might be able to use it for more limited
* deductions , if there are no lower outer joins that delay its
* application . If so , consider adding it to the lists of set - aside
* clauses .
*/
maybe_equivalence = false ;
maybe_outer_join = ! check_outerjoin_delay ( root , & relids ) ;
/*
* Now force the qual to be evaluated exactly at the level of joining
* corresponding to the outer join . We cannot let it get pushed down
* into the nonnullable side , since then we ' d produce no output rows ,
* rather than the intended single null - extended row , for any
* nonnullable - side rows failing the qual .
*
* ( Do this step after calling check_outerjoin_delay , because that
* trashes relids . )
*/
Assert ( ojscope ) ;
relids = ojscope ;
outerjoin_delayed = true ;
Assert ( ! pseudoconstant ) ;
/*
* We can ' t use such a clause to deduce equijoin ( the left and right
* sides might be unequal above the join because one of them has gone
* to NULL ) . . . but we might be able to use it for more limited
* purposes . Note : for the current uses of deductions from an
* outer - join clause , it seems safe to make the deductions even when
* the clause is below a higher - level outer join ; so we do not check
* below_outer_join here .
*/
maybe_equijoin = false ;
maybe_outer_join = true ;
}
else
{
/*
* For a non - outer - join qual , we can evaluate the qual as soon as ( 1 )
* we have all the rels it mentions , and ( 2 ) we are at or above any
* outer joins that can null any of these rels and are below the
* syntactic location of the given qual . We must enforce ( 2 ) because
* pushing down such a clause below the OJ might cause the OJ to emit
* null - extended rows that should not have been formed , or that should
* have been rejected by the clause . ( This is only an issue for
* non - strict quals , since if we can prove a qual mentioning only
* nullable rels is strict , we ' d have reduced the outer join to an
* inner join in reduce_outer_joins ( ) . )
*
* To enforce ( 2 ) , scan the oj_info_list and merge the required - relid
* sets of any such OJs into the clause ' s own reference list . At the
* time we are called , the oj_info_list contains only outer joins
* below this qual . We have to repeat the scan until no new relids
* get added ; this ensures that the qual is suitably delayed regardless
* of the order in which OJs get executed . As an example , if we have
* one OJ with LHS = A , RHS = B , and one with LHS = B , RHS = C , it is implied
* that these can be done in either order ; if the B / C join is done
* first then the join to A can null C , so a qual actually mentioning
* only C cannot be applied below the join to A .
*/
bool found_some ;
outerjoin_delayed = false ;
do {
ListCell * l ;
found_some = false ;
foreach ( l , root - > oj_info_list )
{
OuterJoinInfo * ojinfo = ( OuterJoinInfo * ) lfirst ( l ) ;
/* do we have any nullable rels of this OJ? */
if ( bms_overlap ( relids , ojinfo - > min_righthand ) | |
( ojinfo - > is_full_join & &
bms_overlap ( relids , ojinfo - > min_lefthand ) ) )
{
/* yes; do we have all its rels? */
if ( ! bms_is_subset ( ojinfo - > min_lefthand , relids ) | |
! bms_is_subset ( ojinfo - > min_righthand , relids ) )
{
/* no, so add them in */
relids = bms_add_members ( relids ,
ojinfo - > min_lefthand ) ;
relids = bms_add_members ( relids ,
ojinfo - > min_righthand ) ;
outerjoin_delayed = true ;
/* we'll need another iteration */
found_some = true ;
}
}
}
} while ( found_some ) ;
/* Normal qual clause; check to see if must be delayed by outer join */
outerjoin_delayed = check_outerjoin_delay ( root , & relids ) ;
if ( outerjoin_delayed )
{
@ -816,26 +760,27 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
* Because application of the qual will be delayed by outer join ,
* we mustn ' t assume its vars are equal everywhere .
*/
maybe_equijoin = false ;
maybe_equivalence = false ;
}
else
{
/*
* Qual is not delayed by any lower outer - join restriction . If it
* is not itself below or within an outer join , we can consider it
* " valid everywhere " , so consider feeding it to the equijoin
* machinery . ( If it is within an outer join , we can ' t consider
* it " valid everywhere " : once the contained variables have gone
* to NULL , we ' d be asserting things like NULL = NULL , which is
* not true . )
* Qual is not delayed by any lower outer - join restriction , so
* we can consider feeding it to the equivalence machinery .
* However , if it ' s itself within an outer - join clause , treat it
* as though it appeared below that outer join ( note that we can
* only get here when the clause references only nullable - side
* rels ) .
*/
if ( ! below_outer_join & & outerjoin_nonnullable = = NULL )
maybe_equijoin = true ;
else
maybe_equijoin = false ;
maybe_equivalence = true ;
if ( outerjoin_nonnullable ! = NULL )
below_outer_join = true ;
}
/* Since it doesn't mention the LHS, it's certainly not an OJ clause */
/*
* Since it doesn ' t mention the LHS , it ' s certainly not useful as a
* set - aside OJ clause , even if it ' s in an OJ .
*/
maybe_outer_join = false ;
}
@ -860,118 +805,65 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
relids ) ;
/*
* Figure out where to attach it .
* If it ' s a join clause ( either naturally , or because delayed by
* outer - join rules ) , add vars used in the clause to targetlists of
* their relations , so that they will be emitted by the plan nodes that
* scan those relations ( else they won ' t be available at the join node ! ) .
*
* Note : if the clause gets absorbed into an EquivalenceClass then this
* may be unnecessary , but for now we have to do it to cover the case
* where the EC becomes ec_broken and we end up reinserting the original
* clauses into the plan .
*/
switch ( bms_membership ( relids ) )
if ( bms_membership ( relids ) = = BMS_MULTIPLE )
{
case BMS_SINGLETON :
/*
* There is only one relation participating in ' clause ' , so
* ' clause ' is a restriction clause for that relation .
*/
rel = find_base_rel ( root , bms_singleton_member ( relids ) ) ;
/*
* Check for a " mergejoinable " clause even though it ' s not a join
* clause . This is so that we can recognize that " a.x = a.y "
* makes x and y eligible to be considered equal , even when they
* belong to the same rel . Without this , we would not recognize
* that " a.x = a.y AND a.x = b.z AND a.y = c.q " allows us to
* consider z and q equal after their rels are joined .
*/
check_mergejoinable ( restrictinfo ) ;
List * vars = pull_var_clause ( clause , false ) ;
/*
* If the clause was deduced from implied equality , check to see
* whether it is redundant with restriction clauses we already
* have for this rel . Note we cannot apply this check to
* user - written clauses , since we haven ' t found the canonical
* pathkey sets yet while processing user clauses . ( NB : no
* comparable check is done in the join - clause case ; redundancy
* will be detected when the join clause is moved into a join
* rel ' s restriction list . )
*/
if ( ! is_deduced | |
! qual_is_redundant ( root , restrictinfo ,
rel - > baserestrictinfo ) )
{
/* Add clause to rel's restriction list */
rel - > baserestrictinfo = lappend ( rel - > baserestrictinfo ,
restrictinfo ) ;
}
break ;
case BMS_MULTIPLE :
/*
* ' clause ' is a join clause , since there is more than one rel in
* the relid set .
*/
/*
* Check for hash or mergejoinable operators .
*
* We don ' t bother setting the hashjoin info if we ' re not going to
* need it . We do want to know about mergejoinable ops in all
* cases , however , because we use mergejoinable ops for other
* purposes such as detecting redundant clauses .
*/
check_mergejoinable ( restrictinfo ) ;
if ( enable_hashjoin )
check_hashjoinable ( restrictinfo ) ;
/*
* Add clause to the join lists of all the relevant relations .
*/
add_join_clause_to_rels ( root , restrictinfo , relids ) ;
/*
* Add vars used in the join clause to targetlists of their
* relations , so that they will be emitted by the plan nodes that
* scan those relations ( else they won ' t be available at the join
* node ! ) .
*/
vars = pull_var_clause ( clause , false ) ;
add_vars_to_targetlist ( root , vars , relids ) ;
list_free ( vars ) ;
break ;
default :
/*
* ' clause ' references no rels , and therefore we have no place to
* attach it . Shouldn ' t get here if callers are working properly .
*/
elog ( ERROR , " cannot cope with variable-free clause " ) ;
break ;
add_vars_to_targetlist ( root , vars , relids ) ;
list_free ( vars ) ;
}
/*
* If the clause has a mergejoinable operator , we may be able to deduce
* more things from it under the principle of transitivity .
* We check " mergejoinability " of every clause , not only join clauses ,
* because we want to know about equivalences between vars of the same
* relation , or between vars and consts .
*/
check_mergejoinable ( restrictinfo ) ;
/*
* If it is a true equivalence clause , send it to the EquivalenceClass
* machinery . We do * not * attach it directly to any restriction or join
* lists . The EC code will propagate it to the appropriate places later .
*
* If it is not an outer - join qualification nor bubbled up due to an outer
* join , then the two sides represent equivalent PathKeyItems for path
* keys : any path that is sorted by one side will also be sorted by the
* other ( as soon as the two rels are joined , that is ) . Pass such clauses
* to add_equijoined_keys .
* If the clause has a mergejoinable operator and is not outerjoin - delayed ,
* yet isn ' t an equivalence because it is an outer - join clause , the EC
* code may yet be able to do something with it . We add it to appropriate
* lists for further consideration later . Specifically :
*
* If it is a left or right outer - join qualification that relates the two
* sides of the outer join ( no funny business like leftvar1 = leftvar2 +
* rightvar ) , we add it to root - > left_join_clauses or
* If it is a left or right outer - join qualification that relates the
* two sides of the outer join ( no funny business like leftvar1 =
* leftvar2 + rightvar ) , we add it to root - > left_join_clauses or
* root - > right_join_clauses according to which side the nonnullable
* variable appears on .
*
* If it is a full outer - join qualification , we add it to
* root - > full_join_clauses . ( Ideally we ' d discard cases that aren ' t
* leftvar = rightvar , as we do for left / right joins , but this routine
* doesn ' t have the info needed to do that ; and the current usage of the
* full_join_clauses list doesn ' t require that , so it ' s not currently
* worth complicating this routine ' s API to make it possible . )
* doesn ' t have the info needed to do that ; and the current usage of
* the full_join_clauses list doesn ' t require that , so it ' s not
* currently worth complicating this routine ' s API to make it possible . )
*
* If none of the above hold , pass it off to
* distribute_restrictinfo_to_rels ( ) .
*/
if ( restrictinfo - > mergejoinoperator ! = InvalidOid )
if ( restrictinfo - > mergeopfamilies )
{
if ( maybe_equijoin )
add_equijoined_keys ( root , restrictinfo ) ;
if ( maybe_equivalence )
{
if ( process_equivalence ( root , restrictinfo , below_outer_join ) )
return ;
/* EC rejected it, so pass to distribute_restrictinfo_to_rels */
}
else if ( maybe_outer_join & & restrictinfo - > can_join )
{
if ( bms_is_subset ( restrictinfo - > left_relids ,
@ -982,8 +874,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
/* we have outervar = innervar */
root - > left_join_clauses = lappend ( root - > left_join_clauses ,
restrictinfo ) ;
return ;
}
else if ( bms_is_subset ( restrictinfo - > right_relids ,
if ( bms_is_subset ( restrictinfo - > right_relids ,
outerjoin_nonnullable ) & &
! bms_overlap ( restrictinfo - > left_relids ,
outerjoin_nonnullable ) )
@ -991,166 +884,213 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
/* we have innervar = outervar */
root - > right_join_clauses = lappend ( root - > right_join_clauses ,
restrictinfo ) ;
return ;
}
else if ( bms_equal ( outerjoin_nonnullable , qualscope ) )
if ( bms_equal ( outerjoin_nonnullable , qualscope ) )
{
/* FULL JOIN (above tests cannot match in this case) */
root - > full_join_clauses = lappend ( root - > full_join_clauses ,
restrictinfo ) ;
return ;
}
}
}
/* No EC special case applies, so push it into the clause lists */
distribute_restrictinfo_to_rels ( root , restrictinfo ) ;
}
/*
* process_implied_equality
* Check to see whether we already have a restrictinfo item that says
* item1 = item2 , and create one if not ; or if delete_it is true ,
* remove any such restrictinfo item .
* check_outerjoin_delay
* Detect whether a qual referencing the given relids must be delayed
* in application due to the presence of a lower outer join .
*
* This processing is a consequence of transitivity of mergejoin equality :
* if we have mergejoinable clauses A = B and B = C , we can deduce A = C
* ( where = is an appropriate mergejoinable operator ) . See path / pathkeys . c
* for more details .
* If so , add relids to * relids_p to reflect the lowest safe level for
* evaluating the qual , and return TRUE .
*
* For a non - outer - join qual , we can evaluate the qual as soon as ( 1 ) we have
* all the rels it mentions , and ( 2 ) we are at or above any outer joins that
* can null any of these rels and are below the syntactic location of the
* given qual . We must enforce ( 2 ) because pushing down such a clause below
* the OJ might cause the OJ to emit null - extended rows that should not have
* been formed , or that should have been rejected by the clause . ( This is
* only an issue for non - strict quals , since if we can prove a qual mentioning
* only nullable rels is strict , we ' d have reduced the outer join to an inner
* join in reduce_outer_joins ( ) . )
*
* To enforce ( 2 ) , scan the oj_info_list and merge the required - relid sets of
* any such OJs into the clause ' s own reference list . At the time we are
* called , the oj_info_list contains only outer joins below this qual . We
* have to repeat the scan until no new relids get added ; this ensures that
* the qual is suitably delayed regardless of the order in which OJs get
* executed . As an example , if we have one OJ with LHS = A , RHS = B , and one with
* LHS = B , RHS = C , it is implied that these can be done in either order ; if the
* B / C join is done first then the join to A can null C , so a qual actually
* mentioning only C cannot be applied below the join to A .
*
* For an outer - join qual , this isn ' t going to determine where we place the
* qual , but we need to determine outerjoin_delayed anyway so we can decide
* whether the qual is potentially useful for equivalence deductions .
*/
void
process_implied_equality ( PlannerInfo * root ,
Node * item1 , Node * item2 ,
Oid sortop1 , Oid sortop2 ,
Relids item1_relids , Relids item2_relids ,
bool delete_it )
static bool
check_outerjoin_delay ( PlannerInfo * root , Relids * relids_p )
{
Relids relids ;
BMS_Membership membership ;
RelOptInfo * rel1 ;
List * restrictlist ;
ListCell * itm ;
Oid ltype ,
rtype ;
Operator eq_operator ;
Form_pg_operator pgopform ;
Expr * clause ;
/* Get set of relids referenced in the two expressions */
relids = bms_union ( item1_relids , item2_relids ) ;
membership = bms_membership ( relids ) ;
/*
* generate_implied_equalities ( ) shouldn ' t call me on two constants .
*/
Assert ( membership ! = BMS_EMPTY_SET ) ;
/*
* If the exprs involve a single rel , we need to look at that rel ' s
* baserestrictinfo list . If multiple rels , we can scan the joininfo list
* of any of ' em .
*/
if ( membership = = BMS_SINGLETON )
{
rel1 = find_base_rel ( root , bms_singleton_member ( relids ) ) ;
restrictlist = rel1 - > baserestrictinfo ;
}
else
{
Relids other_rels ;
int first_rel ;
/* Copy relids, find and remove one member */
other_rels = bms_copy ( relids ) ;
first_rel = bms_first_member ( other_rels ) ;
bms_free ( other_rels ) ;
Relids relids = * relids_p ;
bool outerjoin_delayed ;
bool found_some ;
rel1 = find_base_rel ( root , first_rel ) ;
restrictlist = rel1 - > joininfo ;
}
outerjoin_delayed = false ;
do {
ListCell * l ;
/*
* Scan to see if equality is already known . If so , we ' re done in the add
* case , and done after removing it in the delete case .
*/
foreach ( itm , restrictlist )
{
RestrictInfo * restrictinfo = ( RestrictInfo * ) lfirst ( itm ) ;
Node * left ,
* right ;
if ( restrictinfo - > mergejoinoperator = = InvalidOid )
continue ; /* ignore non-mergejoinable clauses */
/* We now know the restrictinfo clause is a binary opclause */
left = get_leftop ( restrictinfo - > clause ) ;
right = get_rightop ( restrictinfo - > clause ) ;
if ( ( equal ( item1 , left ) & & equal ( item2 , right ) ) | |
( equal ( item2 , left ) & & equal ( item1 , right ) ) )
found_some = false ;
foreach ( l , root - > oj_info_list )
{
/* found a matching clause */
if ( delete_it )
OuterJoinInfo * ojinfo = ( OuterJoinInfo * ) lfirst ( l ) ;
/* do we reference any nullable rels of this OJ? */
if ( bms_overlap ( relids , ojinfo - > min_righthand ) | |
( ojinfo - > is_full_join & &
bms_overlap ( relids , ojinfo - > min_lefthand ) ) )
{
if ( membership = = BMS_SINGLETON )
{
/* delete it from local restrictinfo list */
rel1 - > baserestrictinfo = list_delete_ptr ( rel1 - > baserestrictinfo ,
restrictinfo ) ;
}
else
/* yes; have we included all its rels in relids? */
if ( ! bms_is_subset ( ojinfo - > min_lefthand , relids ) | |
! bms_is_subset ( ojinfo - > min_righthand , relids ) )
{
/* let joininfo.c do it */
remove_join_clause_from_rels ( root , restrictinfo , relids ) ;
/* no, so add them in */
relids = bms_add_members ( relids , ojinfo - > min_lefthand ) ;
relids = bms_add_members ( relids , ojinfo - > min_righthand ) ;
outerjoin_delayed = true ;
/* we'll need another iteration */
found_some = true ;
}
}
return ; /* done */
}
}
} while ( found_some ) ;
/* Didn't find it. Done if deletion requested */
if ( delete_it )
return ;
* relids_p = relids ;
return outerjoin_delayed ;
}
/*
* This equality is new information , so construct a clause representing it
* to add to the query data structures .
*/
ltype = exprType ( item1 ) ;
rtype = exprType ( item2 ) ;
eq_operator = compatible_oper ( NULL , list_make1 ( makeString ( " = " ) ) ,
ltype , rtype ,
true , - 1 ) ;
if ( ! HeapTupleIsValid ( eq_operator ) )
/*
* distribute_restrictinfo_to_rels
* Push a completed RestrictInfo into the proper restriction or join
* clause list ( s ) .
*
* This is the last step of distribute_qual_to_rels ( ) for ordinary qual
* clauses . Clauses that are interesting for equivalence - class processing
* are diverted to the EC machinery , but may ultimately get fed back here .
*/
void
distribute_restrictinfo_to_rels ( PlannerInfo * root ,
RestrictInfo * restrictinfo )
{
Relids relids = restrictinfo - > required_relids ;
RelOptInfo * rel ;
switch ( bms_membership ( relids ) )
{
/*
* Would it be safe to just not add the equality to the query if we
* have no suitable equality operator for the combination of
* datatypes ? NO , because sortkey selection may screw up anyway .
*/
ereport ( ERROR ,
( errcode ( ERRCODE_UNDEFINED_FUNCTION ) ,
errmsg ( " could not identify an equality operator for types %s and %s " ,
format_type_be ( ltype ) , format_type_be ( rtype ) ) ) ) ;
case BMS_SINGLETON :
/*
* There is only one relation participating in the clause , so
* it is a restriction clause for that relation .
*/
rel = find_base_rel ( root , bms_singleton_member ( relids ) ) ;
/* Add clause to rel's restriction list */
rel - > baserestrictinfo = lappend ( rel - > baserestrictinfo ,
restrictinfo ) ;
break ;
case BMS_MULTIPLE :
/*
* The clause is a join clause , since there is more than one rel
* in its relid set .
*/
/*
* Check for hashjoinable operators . ( We don ' t bother setting
* the hashjoin info if we ' re not going to need it . )
*/
if ( enable_hashjoin )
check_hashjoinable ( restrictinfo ) ;
/*
* Add clause to the join lists of all the relevant relations .
*/
add_join_clause_to_rels ( root , restrictinfo , relids ) ;
break ;
default :
/*
* clause references no rels , and therefore we have no place to
* attach it . Shouldn ' t get here if callers are working properly .
*/
elog ( ERROR , " cannot cope with variable-free clause " ) ;
break ;
}
pgopform = ( Form_pg_operator ) GETSTRUCT ( eq_operator ) ;
}
/*
* Let ' s just make sure this appears to be a compatible operator .
*
* XXX needs work
*/
if ( pgopform - > oprresult ! = BOOLOID )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_FUNCTION_DEFINITION ) ,
errmsg ( " equality operator for types %s and %s should be merge-joinable, but isn't " ,
format_type_be ( ltype ) , format_type_be ( rtype ) ) ) ) ;
/*
* process_implied_equality
* Create a restrictinfo item that says " item1 op item2 " , and push it
* into the appropriate lists . ( In practice opno is always a btree
* equality operator . )
*
* " qualscope " is the nominal syntactic level to impute to the restrictinfo .
* This must contain at least all the rels used in the expressions , but it
* is used only to set the qual application level when both exprs are
* variable - free . Otherwise the qual is applied at the lowest join level
* that provides all its variables .
*
* " both_const " indicates whether both items are known pseudo - constant ;
* in this case it is worth applying eval_const_expressions ( ) in case we
* can produce constant TRUE or constant FALSE . ( Otherwise it ' s not ,
* because the expressions went through eval_const_expressions already . )
*
* This is currently used only when an EquivalenceClass is found to
* contain pseudoconstants . See path / pathkeys . c for more details .
*/
void
process_implied_equality ( PlannerInfo * root ,
Oid opno ,
Expr * item1 ,
Expr * item2 ,
Relids qualscope ,
bool below_outer_join ,
bool both_const )
{
Expr * clause ;
/*
* Now we can build the new clause . Copy to ensure it shares no
* substructure with original ( this is necessary in case there are
* subselects in there . . . )
* Build the new clause . Copy to ensure it shares no substructure with
* original ( this is necessary in case there are subselects in there . . . )
*/
clause = make_opclause ( oprid ( eq_operator ) , /* opno */
clause = make_opclause ( opn o ,
BOOLOID , /* opresulttype */
false , /* opretset */
( Expr * ) copyObject ( item1 ) ,
( Expr * ) copyObject ( item2 ) ) ;
ReleaseSysCache ( eq_operator ) ;
/* If both constant, try to reduce to a boolean constant. */
if ( both_const )
{
clause = ( Expr * ) eval_const_expressions ( ( Node * ) clause ) ;
/* If we produced const TRUE, just drop the clause */
if ( clause & & IsA ( clause , Const ) )
{
Const * cclause = ( Const * ) clause ;
Assert ( cclause - > consttype = = BOOLOID ) ;
if ( ! cclause - > constisnull & & DatumGetBool ( cclause - > constvalue ) )
return ;
}
}
/* 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 .
@ -1159,119 +1099,53 @@ process_implied_equality(PlannerInfo *root,
* taken for an original JOIN / ON clause .
*/
distribute_qual_to_rels ( root , ( Node * ) clause ,
true , true , false , relids , NULL , NULL ) ;
true , true , below_outer_join ,
qualscope , NULL , NULL ) ;
}
/*
* qual_is_redundant
* Detect whether an implied - equality qual that turns out to be a
* restriction clause for a single base relation is redundant with
* already - known restriction clauses for that rel . This occurs with ,
* for example ,
* SELECT * FROM tab WHERE f1 = f2 AND f2 = f3 ;
* We need to suppress the redundant condition to avoid computing
* too - small selectivity , not to mention wasting time at execution .
* build_implied_join_equality - - - build a RestrictInfo for a derived equality
*
* Note : quals of the form " var = const " are never considered redundant ,
* only those of the form " var = var " . This is needed because when we
* have constants in an implied - equality set , we use a different strategy
* that suppresses all " var = var " deductions . We must therefore keep
* all the " var = const " quals .
* This overlaps the functionality of process_implied_equality ( ) , but we
* must return the RestrictInfo , not push it into the joininfo tree .
*/
static bool
qual_is_redundant ( PlannerInfo * root ,
RestrictInfo * restrictinfo ,
List * restrictlist )
RestrictInfo *
build_implied_join_equality ( Oid opno ,
Expr * item1 ,
Expr * item2 ,
Relids qualscope )
{
Node * newleft ;
Node * newright ;
List * oldquals ;
ListCell * olditem ;
List * equalexprs ;
bool someadded ;
/* Never redundant unless vars appear on both sides */
if ( bms_is_empty ( restrictinfo - > left_relids ) | |
bms_is_empty ( restrictinfo - > right_relids ) )
return false ;
newleft = get_leftop ( restrictinfo - > clause ) ;
newright = get_rightop ( restrictinfo - > clause ) ;
/*
* Set cached pathkeys . NB : it is okay to do this now because this
* routine is only invoked while we are generating implied equalities .
* Therefore , the equi_key_list is already complete and so we can
* correctly determine canonical pathkeys .
*/
cache_mergeclause_pathkeys ( root , restrictinfo ) ;
/* If different, say "not redundant" (should never happen) */
if ( restrictinfo - > left_pathkey ! = restrictinfo - > right_pathkey )
return false ;
RestrictInfo * restrictinfo ;
Expr * clause ;
/*
* Scan existing quals to find those referencing same pathkeys . Usually
* there will be few , if any , so build a list of just the interesting
* ones .
* Build the new clause . Copy to ensure it shares no substructure with
* original ( this is necessary in case there are subselects in there . . . )
*/
oldquals = NIL ;
foreach ( olditem , restrictlist )
{
RestrictInfo * oldrinfo = ( RestrictInfo * ) lfirst ( olditem ) ;
clause = make_opclause ( opno ,
BOOLOID , /* opresulttype */
false , /* opretset */
( Expr * ) copyObject ( item1 ) ,
( Expr * ) copyObject ( item2 ) ) ;
if ( oldrinfo - > mergejoinoperator ! = InvalidOid )
{
cache_mergeclause_pathkeys ( root , oldrinfo ) ;
if ( restrictinfo - > left_pathkey = = oldrinfo - > left_pathkey & &
restrictinfo - > right_pathkey = = oldrinfo - > right_pathkey )
oldquals = lcons ( oldrinfo , oldquals ) ;
}
}
if ( oldquals = = NIL )
return false ;
/* Make a copy of qualscope to avoid problems if source EC changes */
qualscope = bms_copy ( qualscope ) ;
/*
* Now , we want to develop a list of exprs that are known equal to the
* left side of the new qual . We traverse the old - quals list repeatedly
* to transitively expand the exprs list . If at any point we find we can
* reach the right - side expr of the new qual , we are done . We give up
* when we can ' t expand the equalexprs list any more .
* Build the RestrictInfo node itself .
*/
equalexprs = list_make1 ( newleft ) ;
do
{
someadded = false ;
/* cannot use foreach here because of possible list_delete */
olditem = list_head ( oldquals ) ;
while ( olditem )
{
RestrictInfo * oldrinfo = ( RestrictInfo * ) lfirst ( olditem ) ;
Node * oldleft = get_leftop ( oldrinfo - > clause ) ;
Node * oldright = get_rightop ( oldrinfo - > clause ) ;
Node * newguy = NULL ;
/* must advance olditem before list_delete possibly pfree's it */
olditem = lnext ( olditem ) ;
if ( list_member ( equalexprs , oldleft ) )
newguy = oldright ;
else if ( list_member ( equalexprs , oldright ) )
newguy = oldleft ;
else
continue ;
if ( equal ( newguy , newright ) )
return true ; /* we proved new clause is redundant */
equalexprs = lcons ( newguy , equalexprs ) ;
someadded = true ;
/*
* Remove this qual from list , since we don ' t need it anymore .
*/
oldquals = list_delete_ptr ( oldquals , oldrinfo ) ;
}
} while ( someadded ) ;
return false ; /* it's not redundant */
restrictinfo = make_restrictinfo ( clause ,
true , /* is_pushed_down */
false , /* outerjoin_delayed */
false , /* pseudoconstant */
qualscope ) ;
/* Set mergejoinability info always, and hashjoinability if enabled */
check_mergejoinable ( restrictinfo ) ;
if ( enable_hashjoin )
check_hashjoinable ( restrictinfo ) ;
return restrictinfo ;
}
@ -1294,10 +1168,7 @@ static void
check_mergejoinable ( RestrictInfo * restrictinfo )
{
Expr * clause = restrictinfo - > clause ;
Oid opno ,
leftOp ,
rightOp ;
Oid opfamily ;
Oid opno ;
if ( restrictinfo - > pseudoconstant )
return ;
@ -1310,16 +1181,13 @@ check_mergejoinable(RestrictInfo *restrictinfo)
if ( op_mergejoinable ( opno ) & &
! contain_volatile_functions ( ( Node * ) clause ) )
{
/* XXX for the moment, continue to force use of particular sortops */
if ( get_op_mergejoin_info ( opno , & leftOp , & rightOp , & opfamily ) )
{
restrictinfo - > mergejoinoperator = opno ;
restrictinfo - > left_sortop = leftOp ;
restrictinfo - > right_sortop = rightOp ;
restrictinfo - > mergeopfamily = opfamily ;
}
}
restrictinfo - > mergeopfamilies = get_mergejoin_opfamilies ( opno ) ;
/*
* Note : op_mergejoinable is just a hint ; if we fail to find the
* operator in any btree opfamilies , mergeopfamilies remains NIL
* and so the clause is not treated as mergejoinable .
*/
}
/*