@ -55,8 +55,9 @@ typedef struct finalize_primnode_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 , Node * testexpr ,
SubLinkType subLinkType , int subLinkId ,
bool adjust_testexpr , bool unknownEqFalse ) ;
Node * testexpr , bool adjust_testexpr ,
bool unknownEqFalse ) ;
static List * generate_subquery_params ( PlannerInfo * root , List * tlist ,
static List * generate_subquery_params ( PlannerInfo * root , List * tlist ,
List * * paramIds ) ;
List * * paramIds ) ;
static List * generate_subquery_vars ( PlannerInfo * root , List * tlist ,
static List * generate_subquery_vars ( PlannerInfo * root , List * tlist ,
@ -407,7 +408,7 @@ get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod,
/*
/*
* Convert a SubLink ( as created by the parser ) into a SubPlan .
* Convert a SubLink ( as created by the parser ) into a SubPlan .
*
*
* We are given the SubLink ' s contained query , type , and testexpr . We are
* We are given the SubLink ' s contained query , type , ID , and testexpr . We are
* also told if this expression appears at top level of a WHERE / HAVING qual .
* also told if this expression appears at top level of a WHERE / HAVING qual .
*
*
* Note : we assume that the testexpr has been AND / OR flattened ( actually ,
* Note : we assume that the testexpr has been AND / OR flattened ( actually ,
@ -415,14 +416,20 @@ get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod,
* implicit - AND form ; and any SubLinks in it should already have been
* implicit - AND form ; and any SubLinks in it should already have been
* converted to SubPlans . The subquery is as yet untouched , however .
* converted to SubPlans . The subquery is as yet untouched , however .
*
*
* The result is whatever we need to substitute in place of the SubLink
* The result is whatever we need to substitute in place of the SubLink node
* node in the executable expression . This will be either the SubPlan
* in the executable expression . If we ' re going to do the subplan as a
* node ( if we have to do the subplan as a subplan ) , or a Param node
* regular subplan , this will be the constructed SubPlan node . If we ' re going
* representing the result of an InitPlan , or a row comparison expression
* to do the subplan as an InitPlan , the SubPlan node instead goes into
* tree containing InitPlan Param nodes .
* root - > init_plans , and what we return here is an expression tree
* representing the InitPlan ' s result : usually just a Param node representing
* a single scalar result , but possibly a row comparison tree containing
* multiple Param nodes , or for a MULTIEXPR subquery a simple NULL constant
* ( since the real output Params are elsewhere in the tree , and the MULTIEXPR
* subquery itself is in a resjunk tlist entry whose value is uninteresting ) .
*/
*/
static Node *
static Node *
make_subplan ( PlannerInfo * root , Query * orig_subquery , SubLinkType subLinkType ,
make_subplan ( PlannerInfo * root , Query * orig_subquery ,
SubLinkType subLinkType , int subLinkId ,
Node * testexpr , bool isTopQual )
Node * testexpr , bool isTopQual )
{
{
Query * subquery ;
Query * subquery ;
@ -452,8 +459,8 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
* first tuple will be retrieved . For ALL and ANY subplans , we will be
* first tuple will be retrieved . For ALL and ANY subplans , we will be
* able to stop evaluating if the test condition fails or matches , so very
* able to stop evaluating if the test condition fails or matches , so very
* often not all the tuples will be retrieved ; for lack of a better idea ,
* often not all the tuples will be retrieved ; for lack of a better idea ,
* specify 50 % retrieval . For EXPR and ROWCOMPARE subplans , use default
* specify 50 % retrieval . For EXPR , MULTIEXPR , and ROWCOMPARE subplans ,
* behavior ( we ' re only expecting one row out , anyway ) .
* use default behavior ( we ' re only expecting one row out , anyway ) .
*
*
* NOTE : if you change these numbers , also change cost_subplan ( ) in
* NOTE : if you change these numbers , also change cost_subplan ( ) in
* path / costsize . c .
* path / costsize . c .
@ -491,7 +498,8 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
/* 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 , testexpr , true , isTopQual ) ;
subLinkType , subLinkId ,
testexpr , true , 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
@ -536,7 +544,8 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
/* OK, convert to SubPlan format. */
/* OK, convert to SubPlan format. */
hashplan = ( SubPlan * ) build_subplan ( root , plan , subroot ,
hashplan = ( SubPlan * ) build_subplan ( root , plan , subroot ,
plan_params ,
plan_params ,
ANY_SUBLINK , newtestexpr ,
ANY_SUBLINK , 0 ,
newtestexpr ,
false , true ) ;
false , true ) ;
/* Check we got what we expected */
/* Check we got what we expected */
Assert ( IsA ( hashplan , SubPlan ) ) ;
Assert ( IsA ( hashplan , SubPlan ) ) ;
@ -559,14 +568,15 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
/*
/*
* Build a SubPlan node given the raw inputs - - - subroutine for make_subplan
* Build a SubPlan node given the raw inputs - - - subroutine for make_subplan
*
*
* Returns either the SubPlan , or an expression using initplan output Params ,
* Returns either the SubPlan , or a replacement expression if we decide to
* as explained in the comments for make_subplan .
* make it an InitPlan , as explained in the comments for make_subplan .
*/
*/
static Node *
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 , Node * testexpr ,
SubLinkType subLinkType , int subLinkId ,
bool adjust_testexpr , bool unknownEqFalse )
Node * testexpr , bool adjust_testexpr ,
bool unknownEqFalse )
{
{
Node * result ;
Node * result ;
SubPlan * splan ;
SubPlan * splan ;
@ -615,12 +625,15 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
}
}
/*
/*
* Un - correlated or undirect correlated plans of EXISTS , EXPR , ARRAY , or
* Un - correlated or undirect correlated plans of EXISTS , EXPR , ARRAY ,
* ROWCOMPARE types can be used as initPlans . For EXISTS , EXPR , or ARRAY ,
* ROWCOMPARE , or MULTIEXPR types can be used as initPlans . For EXISTS ,
* we just produce a Param referring to the result of evaluating the
* EXPR , or ARRAY , we return a Param referring to the result of evaluating
* initPlan . For ROWCOMPARE , we must modify the testexpr tree to contain
* the initPlan . For ROWCOMPARE , we must modify the testexpr tree to
* PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted by the
* contain PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted
* parser .
* by the parser , and then return that tree . For MULTIEXPR , we return a
* null constant : the resjunk targetlist item containing the SubLink does
* not need to return anything useful , since the referencing Params are
* elsewhere .
*/
*/
if ( splan - > parParam = = NIL & & subLinkType = = EXISTS_SUBLINK )
if ( splan - > parParam = = NIL & & subLinkType = = EXISTS_SUBLINK )
{
{
@ -687,6 +700,42 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
* plan ' s expression tree ; it is not kept in the initplan node .
* plan ' s expression tree ; it is not kept in the initplan node .
*/
*/
}
}
else if ( subLinkType = = MULTIEXPR_SUBLINK )
{
/*
* Whether it ' s an initplan or not , it needs to set a PARAM_EXEC Param
* for each output column .
*/
List * params ;
Assert ( testexpr = = NULL ) ;
params = generate_subquery_params ( root ,
plan - > targetlist ,
& splan - > setParam ) ;
/*
* Save the list of replacement Params in the n ' th cell of
* root - > multiexpr_params ; setrefs . c will use it to replace
* PARAM_MULTIEXPR Params .
*/
while ( list_length ( root - > multiexpr_params ) < subLinkId )
root - > multiexpr_params = lappend ( root - > multiexpr_params , NIL ) ;
lc = list_nth_cell ( root - > multiexpr_params , subLinkId - 1 ) ;
Assert ( lfirst ( lc ) = = NIL ) ;
lfirst ( lc ) = params ;
/* It can be an initplan if there are no parParams. */
if ( splan - > parParam = = NIL )
{
isInitPlan = true ;
result = ( Node * ) makeNullConst ( RECORDOID , - 1 , InvalidOid ) ;
}
else
{
isInitPlan = false ;
result = ( Node * ) splan ;
}
}
else
else
{
{
/*
/*
@ -760,25 +809,22 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
splan - > plan_id ) ;
splan - > plan_id ) ;
/* Label the subplan for EXPLAIN purposes */
/* Label the subplan for EXPLAIN purposes */
if ( isInitPlan )
splan - > plan_name = palloc ( 32 + 12 * list_length ( splan - > setParam ) ) ;
sprintf ( splan - > plan_name , " %s %d " ,
isInitPlan ? " InitPlan " : " SubPlan " ,
splan - > plan_id ) ;
if ( splan - > setParam )
{
{
ListCell * lc ;
char * ptr = splan - > plan_name + strlen ( splan - > plan_name ) ;
int offset ;
splan - > plan_name = palloc ( 32 + 12 * list_length ( splan - > setParam ) ) ;
ptr + = sprintf ( ptr , " (returns " ) ;
sprintf ( splan - > plan_name , " InitPlan %d (returns " , splan - > plan_id ) ;
offset = strlen ( splan - > plan_name ) ;
foreach ( lc , splan - > setParam )
foreach ( lc , splan - > setParam )
{
{
sprintf ( splan - > plan_name + offset , " $%d%s " ,
ptr + = sprintf ( ptr , " $%d%s " ,
lfirst_int ( lc ) ,
lfirst_int ( lc ) ,
lnext ( lc ) ? " , " : " " ) ;
lnext ( lc ) ? " , " : " ) " ) ;
offset + = strlen ( splan - > plan_name + offset ) ;
}
}
sprintf ( splan - > plan_name + offset , " ) " ) ;
}
}
else
splan - > plan_name = psprintf ( " SubPlan %d " , splan - > plan_id ) ;
/* Lastly, fill in the cost estimates for use later */
/* Lastly, fill in the cost estimates for use later */
cost_subplan ( root , splan , plan ) ;
cost_subplan ( root , splan , plan ) ;
@ -1816,6 +1862,7 @@ process_sublinks_mutator(Node *node, process_sublinks_context *context)
return make_subplan ( context - > root ,
return make_subplan ( context - > root ,
( Query * ) sublink - > subselect ,
( Query * ) sublink - > subselect ,
sublink - > subLinkType ,
sublink - > subLinkType ,
sublink - > subLinkId ,
testexpr ,
testexpr ,
context - > isTopQual ) ;
context - > isTopQual ) ;
}
}