Fix subselect.c to avoid assuming that a SubLink's testexpr references each
subquery output column exactly once left-to-right. Although this is the case in the original parser output, it might not be so after rewriting and constant-folding, as illustrated by bug #3882 from Jan Mate. Instead scan the subquery's target list to obtain needed per-column information; this is duplicative of what the parser did, but only a couple dozen lines need be copied, and we can clean up a couple of notational uglinesses. Bug was introduced in 8.2 as part of revision of SubLink representation.
This commit is contained in:
parent
0df7717faa
commit
a44174cf90
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.128 2008/01/01 19:45:50 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.129 2008/01/17 20:35:27 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -35,8 +35,7 @@
|
|||||||
typedef struct convert_testexpr_context
|
typedef struct convert_testexpr_context
|
||||||
{
|
{
|
||||||
PlannerInfo *root;
|
PlannerInfo *root;
|
||||||
int rtindex; /* RT index for Vars, or 0 for Params */
|
List *subst_nodes; /* Nodes to substitute for Params */
|
||||||
List *righthandIds; /* accumulated list of Vars or Param IDs */
|
|
||||||
} convert_testexpr_context;
|
} convert_testexpr_context;
|
||||||
|
|
||||||
typedef struct process_sublinks_context
|
typedef struct process_sublinks_context
|
||||||
@ -53,10 +52,13 @@ typedef struct finalize_primnode_context
|
|||||||
} finalize_primnode_context;
|
} finalize_primnode_context;
|
||||||
|
|
||||||
|
|
||||||
|
static List *generate_subquery_params(PlannerInfo *root, List *tlist,
|
||||||
|
List **paramIds);
|
||||||
|
static List *generate_subquery_vars(PlannerInfo *root, List *tlist,
|
||||||
|
Index varno);
|
||||||
static Node *convert_testexpr(PlannerInfo *root,
|
static Node *convert_testexpr(PlannerInfo *root,
|
||||||
Node *testexpr,
|
Node *testexpr,
|
||||||
int rtindex,
|
List *subst_nodes);
|
||||||
List **righthandIds);
|
|
||||||
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(SubLink *slink, SubPlan *node, Plan *plan);
|
static bool subplan_is_hashable(SubLink *slink, SubPlan *node, Plan *plan);
|
||||||
@ -374,10 +376,14 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
|
|||||||
else if (splan->parParam == NIL && slink->subLinkType == ROWCOMPARE_SUBLINK)
|
else if (splan->parParam == NIL && slink->subLinkType == ROWCOMPARE_SUBLINK)
|
||||||
{
|
{
|
||||||
/* Adjust the Params */
|
/* Adjust the Params */
|
||||||
|
List *params;
|
||||||
|
|
||||||
|
params = generate_subquery_params(root,
|
||||||
|
plan->targetlist,
|
||||||
|
&splan->paramIds);
|
||||||
result = convert_testexpr(root,
|
result = convert_testexpr(root,
|
||||||
testexpr,
|
testexpr,
|
||||||
0,
|
params);
|
||||||
&splan->paramIds);
|
|
||||||
splan->setParam = list_copy(splan->paramIds);
|
splan->setParam = list_copy(splan->paramIds);
|
||||||
isInitPlan = true;
|
isInitPlan = true;
|
||||||
|
|
||||||
@ -388,14 +394,17 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
List *params;
|
||||||
List *args;
|
List *args;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
|
||||||
/* Adjust the Params */
|
/* Adjust the Params */
|
||||||
|
params = generate_subquery_params(root,
|
||||||
|
plan->targetlist,
|
||||||
|
&splan->paramIds);
|
||||||
splan->testexpr = convert_testexpr(root,
|
splan->testexpr = convert_testexpr(root,
|
||||||
testexpr,
|
testexpr,
|
||||||
0,
|
params);
|
||||||
&splan->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
|
||||||
@ -482,17 +491,76 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* generate_subquery_params: build a list of Params representing the output
|
||||||
|
* columns of a sublink's sub-select, given the sub-select's targetlist.
|
||||||
|
*
|
||||||
|
* We also return an integer list of the paramids of the Params.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds)
|
||||||
|
{
|
||||||
|
List *result;
|
||||||
|
List *ids;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
result = ids = NIL;
|
||||||
|
foreach(lc, tlist)
|
||||||
|
{
|
||||||
|
TargetEntry *tent = (TargetEntry *) lfirst(lc);
|
||||||
|
Param *param;
|
||||||
|
|
||||||
|
if (tent->resjunk)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
param = generate_new_param(root,
|
||||||
|
exprType((Node *) tent->expr),
|
||||||
|
exprTypmod((Node *) tent->expr));
|
||||||
|
result = lappend(result, param);
|
||||||
|
ids = lappend_int(ids, param->paramid);
|
||||||
|
}
|
||||||
|
|
||||||
|
*paramIds = ids;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* generate_subquery_vars: build a list of Vars representing the output
|
||||||
|
* columns of a sublink's sub-select, given the sub-select's targetlist.
|
||||||
|
* The Vars have the specified varno (RTE index).
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
generate_subquery_vars(PlannerInfo *root, List *tlist, Index varno)
|
||||||
|
{
|
||||||
|
List *result;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
result = NIL;
|
||||||
|
foreach(lc, tlist)
|
||||||
|
{
|
||||||
|
TargetEntry *tent = (TargetEntry *) lfirst(lc);
|
||||||
|
Var *var;
|
||||||
|
|
||||||
|
if (tent->resjunk)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var = makeVar(varno,
|
||||||
|
tent->resno,
|
||||||
|
exprType((Node *) tent->expr),
|
||||||
|
exprTypmod((Node *) tent->expr),
|
||||||
|
0);
|
||||||
|
result = lappend(result, var);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* convert_testexpr: convert the testexpr given by the parser into
|
* convert_testexpr: convert the testexpr given by the parser into
|
||||||
* actually executable form. This entails replacing PARAM_SUBLINK Params
|
* actually executable form. This entails replacing PARAM_SUBLINK Params
|
||||||
* with Params or Vars representing the results of the sub-select:
|
* with Params or Vars representing the results of the sub-select. The
|
||||||
*
|
* nodes to be substituted are passed in as the List result from
|
||||||
* If rtindex is 0, we build Params to represent the sub-select outputs.
|
* generate_subquery_params or generate_subquery_vars.
|
||||||
* The paramids of the Params created are returned in the *righthandIds list.
|
|
||||||
*
|
|
||||||
* If rtindex is not 0, we build Vars using that rtindex as varno. Copies
|
|
||||||
* of the Var nodes are returned in *righthandIds (this is a bit of a type
|
|
||||||
* cheat, but we can get away with it).
|
|
||||||
*
|
*
|
||||||
* The given testexpr has already been recursively processed by
|
* The given testexpr has already been recursively processed by
|
||||||
* process_sublinks_mutator. Hence it can no longer contain any
|
* process_sublinks_mutator. Hence it can no longer contain any
|
||||||
@ -502,18 +570,13 @@ make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
|
|||||||
static Node *
|
static Node *
|
||||||
convert_testexpr(PlannerInfo *root,
|
convert_testexpr(PlannerInfo *root,
|
||||||
Node *testexpr,
|
Node *testexpr,
|
||||||
int rtindex,
|
List *subst_nodes)
|
||||||
List **righthandIds)
|
|
||||||
{
|
{
|
||||||
Node *result;
|
|
||||||
convert_testexpr_context context;
|
convert_testexpr_context context;
|
||||||
|
|
||||||
context.root = root;
|
context.root = root;
|
||||||
context.rtindex = rtindex;
|
context.subst_nodes = subst_nodes;
|
||||||
context.righthandIds = NIL;
|
return convert_testexpr_mutator(testexpr, &context);
|
||||||
result = convert_testexpr_mutator(testexpr, &context);
|
|
||||||
*righthandIds = context.righthandIds;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Node *
|
static Node *
|
||||||
@ -528,47 +591,17 @@ convert_testexpr_mutator(Node *node,
|
|||||||
|
|
||||||
if (param->paramkind == PARAM_SUBLINK)
|
if (param->paramkind == PARAM_SUBLINK)
|
||||||
{
|
{
|
||||||
/*
|
if (param->paramid <= 0 ||
|
||||||
* We expect to encounter the Params in column-number sequence. We
|
param->paramid > list_length(context->subst_nodes))
|
||||||
* could handle non-sequential order if necessary, but for now
|
|
||||||
* there's no need. (This is also a useful cross-check that we
|
|
||||||
* aren't finding any unexpected Params.)
|
|
||||||
*/
|
|
||||||
if (param->paramid != list_length(context->righthandIds) + 1)
|
|
||||||
elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);
|
elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);
|
||||||
|
|
||||||
if (context->rtindex)
|
|
||||||
{
|
|
||||||
/* Make the Var node representing the subplan's result */
|
|
||||||
Var *newvar;
|
|
||||||
|
|
||||||
newvar = makeVar(context->rtindex,
|
|
||||||
param->paramid,
|
|
||||||
param->paramtype,
|
|
||||||
param->paramtypmod,
|
|
||||||
0);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy it for caller. NB: we need a copy to avoid having
|
* We copy the list item to avoid having doubly-linked
|
||||||
* doubly-linked substructure in the modified parse tree.
|
* substructure in the modified parse tree. This is probably
|
||||||
|
* unnecessary when it's a Param, but be safe.
|
||||||
*/
|
*/
|
||||||
context->righthandIds = lappend(context->righthandIds,
|
return (Node *) copyObject(list_nth(context->subst_nodes,
|
||||||
copyObject(newvar));
|
param->paramid - 1));
|
||||||
return (Node *) newvar;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Make the Param node representing the subplan's result */
|
|
||||||
Param *newparam;
|
|
||||||
|
|
||||||
newparam = generate_new_param(context->root,
|
|
||||||
param->paramtype,
|
|
||||||
param->paramtypmod);
|
|
||||||
/* Record its ID */
|
|
||||||
context->righthandIds = lappend_int(context->righthandIds,
|
|
||||||
newparam->paramid);
|
|
||||||
return (Node *) newparam;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return expression_tree_mutator(node,
|
return expression_tree_mutator(node,
|
||||||
@ -786,20 +819,24 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
|
|||||||
ininfo->in_operators = in_operators;
|
ininfo->in_operators = in_operators;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build the result qual expression. As a side effect,
|
|
||||||
* ininfo->sub_targetlist is filled with a list of Vars representing the
|
* ininfo->sub_targetlist is filled with a list of Vars representing the
|
||||||
* subselect outputs.
|
* subselect outputs.
|
||||||
*/
|
*/
|
||||||
result = convert_testexpr(root,
|
ininfo->sub_targetlist = generate_subquery_vars(root,
|
||||||
sublink->testexpr,
|
subselect->targetList,
|
||||||
rtindex,
|
rtindex);
|
||||||
&ininfo->sub_targetlist);
|
|
||||||
|
|
||||||
Assert(list_length(in_operators) == list_length(ininfo->sub_targetlist));
|
Assert(list_length(in_operators) == list_length(ininfo->sub_targetlist));
|
||||||
|
|
||||||
/* Add the completed node to the query's list */
|
/* Add the completed node to the query's list */
|
||||||
root->in_info_list = lappend(root->in_info_list, ininfo);
|
root->in_info_list = lappend(root->in_info_list, ininfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the result qual expression.
|
||||||
|
*/
|
||||||
|
result = convert_testexpr(root,
|
||||||
|
sublink->testexpr,
|
||||||
|
ininfo->sub_targetlist);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user