Commit a44174cf authored by Tom Lane's avatar Tom Lane

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.
parent 0df7717f
...@@ -7,7 +7,7 @@ ...@@ -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 @@ ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) /*
{ * We copy the list item to avoid having doubly-linked
/* Make the Var node representing the subplan's result */ * substructure in the modified parse tree. This is probably
Var *newvar; * unnecessary when it's a Param, but be safe.
*/
newvar = makeVar(context->rtindex, return (Node *) copyObject(list_nth(context->subst_nodes,
param->paramid, param->paramid - 1));
param->paramtype,
param->paramtypmod,
0);
/*
* Copy it for caller. NB: we need a copy to avoid having
* doubly-linked substructure in the modified parse tree.
*/
context->righthandIds = lappend(context->righthandIds,
copyObject(newvar));
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) ...@@ -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;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment