@ -69,7 +69,7 @@ typedef struct inline_cte_walker_context
static Node * build_subplan ( PlannerInfo * root , Plan * plan , PlannerInfo * subroot ,
static Node * build_subplan ( PlannerInfo * root , Plan * plan , PlannerInfo * subroot ,
List * plan_params ,
List * plan_params ,
SubLinkType subLinkType , int subLinkId ,
SubLinkType subLinkType , int subLinkId ,
Node * testexpr , bool adjust_testexpr ,
Node * testexpr , List * testexpr_paramids ,
bool unknownEqFalse ) ;
bool unknownEqFalse ) ;
static List * generate_subquery_params ( PlannerInfo * root , List * tlist ,
static List * generate_subquery_params ( PlannerInfo * root , List * tlist ,
List * * paramIds ) ;
List * * paramIds ) ;
@ -81,7 +81,8 @@ static Node *convert_testexpr(PlannerInfo *root,
static Node * convert_testexpr_mutator ( Node * node ,
static Node * convert_testexpr_mutator ( Node * node ,
convert_testexpr_context * context ) ;
convert_testexpr_context * context ) ;
static bool subplan_is_hashable ( Plan * plan ) ;
static bool subplan_is_hashable ( Plan * plan ) ;
static bool testexpr_is_hashable ( Node * testexpr ) ;
static bool testexpr_is_hashable ( Node * testexpr , List * param_ids ) ;
static bool test_opexpr_is_hashable ( OpExpr * testexpr , List * param_ids ) ;
static bool hash_ok_operator ( OpExpr * expr ) ;
static bool hash_ok_operator ( OpExpr * expr ) ;
static bool contain_dml ( Node * node ) ;
static bool contain_dml ( Node * node ) ;
static bool contain_dml_walker ( Node * node , void * context ) ;
static bool contain_dml_walker ( Node * node , void * context ) ;
@ -237,7 +238,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
/* And convert to SubPlan or InitPlan format. */
/* And convert to SubPlan or InitPlan format. */
result = build_subplan ( root , plan , subroot , plan_params ,
result = build_subplan ( root , plan , subroot , plan_params ,
subLinkType , subLinkId ,
subLinkType , subLinkId ,
testexpr , true , isTopQual ) ;
testexpr , NIL , isTopQual ) ;
/*
/*
* If it ' s a correlated EXISTS with an unimportant targetlist , we might be
* If it ' s a correlated EXISTS with an unimportant targetlist , we might be
@ -291,12 +292,11 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
plan_params ,
plan_params ,
ANY_SUBLINK , 0 ,
ANY_SUBLINK , 0 ,
newtestexpr ,
newtestexpr ,
false , true ) ) ;
paramIds ,
true ) ) ;
/* Check we got what we expected */
/* Check we got what we expected */
Assert ( hashplan - > parParam = = NIL ) ;
Assert ( hashplan - > parParam = = NIL ) ;
Assert ( hashplan - > useHashTable ) ;
Assert ( hashplan - > useHashTable ) ;
/* build_subplan won't have filled in paramIds */
hashplan - > paramIds = paramIds ;
/* Leave it to the executor to decide which plan to use */
/* Leave it to the executor to decide which plan to use */
asplan = makeNode ( AlternativeSubPlan ) ;
asplan = makeNode ( AlternativeSubPlan ) ;
@ -319,7 +319,7 @@ static Node *
build_subplan ( PlannerInfo * root , Plan * plan , PlannerInfo * subroot ,
build_subplan ( PlannerInfo * root , Plan * plan , PlannerInfo * subroot ,
List * plan_params ,
List * plan_params ,
SubLinkType subLinkType , int subLinkId ,
SubLinkType subLinkType , int subLinkId ,
Node * testexpr , bool adjust_testexpr ,
Node * testexpr , List * testexpr_paramids ,
bool unknownEqFalse )
bool unknownEqFalse )
{
{
Node * result ;
Node * result ;
@ -484,10 +484,10 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
else
else
{
{
/*
/*
* Adjust the Params in the testexpr , unless caller said it ' s not
* Adjust the Params in the testexpr , unless caller already took care
* needed .
* of it ( as indicated by passing a list of Param IDs ) .
*/
*/
if ( testexpr & & adjust_ testexpr)
if ( testexpr & & testexpr_paramids = = NIL )
{
{
List * params ;
List * params ;
@ -499,7 +499,10 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
params ) ;
params ) ;
}
}
else
else
{
splan - > testexpr = testexpr ;
splan - > testexpr = testexpr ;
splan - > paramIds = testexpr_paramids ;
}
/*
/*
* We can ' t convert subplans of ALL_SUBLINK or ANY_SUBLINK types to
* We can ' t convert subplans of ALL_SUBLINK or ANY_SUBLINK types to
@ -511,7 +514,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
if ( subLinkType = = ANY_SUBLINK & &
if ( subLinkType = = ANY_SUBLINK & &
splan - > parParam = = NIL & &
splan - > parParam = = NIL & &
subplan_is_hashable ( plan ) & &
subplan_is_hashable ( plan ) & &
testexpr_is_hashable ( splan - > testexpr ) )
testexpr_is_hashable ( splan - > testexpr , splan - > paramIds ) )
splan - > useHashTable = true ;
splan - > useHashTable = true ;
/*
/*
@ -734,24 +737,20 @@ subplan_is_hashable(Plan *plan)
/*
/*
* testexpr_is_hashable : is an ANY SubLink ' s test expression hashable ?
* testexpr_is_hashable : is an ANY SubLink ' s test expression hashable ?
*
* To identify LHS vs RHS of the hash expression , we must be given the
* list of output Param IDs of the SubLink ' s subquery .
*/
*/
static bool
static bool
testexpr_is_hashable ( Node * testexpr )
testexpr_is_hashable ( Node * testexpr , List * param_ids )
{
{
/*
/*
* The testexpr must be a single OpExpr , or an AND - clause containing only
* The testexpr must be a single OpExpr , or an AND - clause containing only
* OpExprs .
* OpExprs , each of which satisfy test_opexpr_is_hashable ( ) .
*
* The combining operators must be hashable and strict . The need for
* hashability is obvious , since we want to use hashing . Without
* strictness , behavior in the presence of nulls is too unpredictable . We
* actually must assume even more than plain strictness : they can ' t yield
* NULL for non - null inputs , either ( see nodeSubplan . c ) . However , hash
* indexes and hash joins assume that too .
*/
*/
if ( testexpr & & IsA ( testexpr , OpExpr ) )
if ( testexpr & & IsA ( testexpr , OpExpr ) )
{
{
if ( hash_ok_operator ( ( OpExpr * ) testexpr ) )
if ( test_opexpr_is_hashable ( ( OpExpr * ) testexpr , param_ids ) )
return true ;
return true ;
}
}
else if ( is_andclause ( testexpr ) )
else if ( is_andclause ( testexpr ) )
@ -764,7 +763,7 @@ testexpr_is_hashable(Node *testexpr)
if ( ! IsA ( andarg , OpExpr ) )
if ( ! IsA ( andarg , OpExpr ) )
return false ;
return false ;
if ( ! hash_ok_operator ( ( OpExpr * ) andarg ) )
if ( ! test_opexpr_is_hashable ( ( OpExpr * ) andarg , param_ids ) )
return false ;
return false ;
}
}
return true ;
return true ;
@ -773,6 +772,40 @@ testexpr_is_hashable(Node *testexpr)
return false ;
return false ;
}
}
static bool
test_opexpr_is_hashable ( OpExpr * testexpr , List * param_ids )
{
/*
* The combining operator must be hashable and strict . The need for
* hashability is obvious , since we want to use hashing . Without
* strictness , behavior in the presence of nulls is too unpredictable . We
* actually must assume even more than plain strictness : it can ' t yield
* NULL for non - null inputs , either ( see nodeSubplan . c ) . However , hash
* indexes and hash joins assume that too .
*/
if ( ! hash_ok_operator ( testexpr ) )
return false ;
/*
* The left and right inputs must belong to the outer and inner queries
* respectively ; hence Params that will be supplied by the subquery must
* not appear in the LHS , and Vars of the outer query must not appear in
* the RHS . ( Ordinarily , this must be true because of the way that the
* parser builds an ANY SubLink ' s testexpr . . . but inlining of functions
* could have changed the expression ' s structure , so we have to check .
* Such cases do not occur often enough to be worth trying to optimize , so
* we don ' t worry about trying to commute the clause or anything like
* that ; we just need to be sure not to build an invalid plan . )
*/
if ( list_length ( testexpr - > args ) ! = 2 )
return false ;
if ( contain_exec_param ( ( Node * ) linitial ( testexpr - > args ) , param_ids ) )
return false ;
if ( contain_var_clause ( ( Node * ) lsecond ( testexpr - > args ) ) )
return false ;
return true ;
}
/*
/*
* Check expression is hashable + strict
* Check expression is hashable + strict
*
*