aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>1999-11-15 02:00:15 +0000
committerTom Lane <tgl@sss.pgh.pa.us>1999-11-15 02:00:15 +0000
commitf68e11f373de8b7b1d19203b8edac1a13a8d406d (patch)
treec4e61de2b9bd9b8dd7c70545dc861de3547294d4 /src/backend/optimizer
parent1ecb129d206f30f5813b01e1f1efe75c06febe49 (diff)
downloadpostgresql-f68e11f373de8b7b1d19203b8edac1a13a8d406d.tar.gz
postgresql-f68e11f373de8b7b1d19203b8edac1a13a8d406d.zip
Implement subselects in target lists. Also, relax requirement that
subselects can only appear on the righthand side of a binary operator. That's still true for quantified predicates like x = ANY (SELECT ...), but a subselect that delivers a single result can now appear anywhere in an expression. This is implemented by changing EXPR_SUBLINK sublinks to represent just the (SELECT ...) expression, without any 'left hand side' or combining operator --- so they're now more like EXISTS_SUBLINK. To handle the case of '(x, y, z) = (SELECT ...)', I added a new sublink type MULTIEXPR_SUBLINK, which acts just like EXPR_SUBLINK used to. But the grammar will only generate one for a multiple-left-hand-side row expression.
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/plan/planmain.c18
-rw-r--r--src/backend/optimizer/plan/planner.c12
-rw-r--r--src/backend/optimizer/plan/subselect.c132
3 files changed, 88 insertions, 74 deletions
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 3b289fb3d0e..7da2ee4c211 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.46 1999/10/07 04:23:06 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.47 1999/11/15 02:00:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -87,9 +87,25 @@ query_planner(Query *root,
tlist = (List *) SS_replace_correlation_vars((Node *) tlist);
qual = (List *) SS_replace_correlation_vars((Node *) qual);
}
+
/* Expand SubLinks to SubPlans */
if (root->hasSubLinks)
+ {
+ tlist = (List *) SS_process_sublinks((Node *) tlist);
qual = (List *) SS_process_sublinks((Node *) qual);
+ if (root->groupClause != NIL)
+ {
+ /*
+ * Check for ungrouped variables passed to subplans.
+ * Note we do NOT do this for subplans in WHERE; it's legal
+ * there because WHERE is evaluated pre-GROUP.
+ */
+ if (check_subplans_for_ungrouped_vars((Node *) tlist,
+ root->groupClause,
+ tlist))
+ elog(ERROR, "Sub-SELECT must use only GROUPed attributes from outer SELECT");
+ }
+ }
/*
* If the query contains no relation references at all, it must be
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fce3800dc49..295d722b6a5 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.70 1999/10/07 04:23:06 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.71 1999/11/15 02:00:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -343,17 +343,11 @@ union_planner(Query *parse)
{
/* Expand SubLinks to SubPlans */
parse->havingQual = SS_process_sublinks(parse->havingQual);
-
- /*
- * Check for ungrouped variables passed to subplans. (Probably
- * this should be done for the targetlist as well??? But we
- * should NOT do it for the WHERE qual, since WHERE is
- * evaluated pre-GROUP.)
- */
+ /* Check for ungrouped variables passed to subplans */
if (check_subplans_for_ungrouped_vars(parse->havingQual,
parse->groupClause,
parse->targetList))
- elog(ERROR, "Sub-SELECT in HAVING clause must use only GROUPed attributes from outer SELECT");
+ elog(ERROR, "Sub-SELECT must use only GROUPed attributes from outer SELECT");
}
}
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 7e5d2be749e..5290c96d5db 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -6,7 +6,7 @@
* Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.24 1999/08/25 23:21:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.25 1999/11/15 02:00:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -173,10 +173,43 @@ make_subplan(SubLink *slink)
}
/*
- * Un-correlated or undirect correlated plans of EXISTS or EXPR types
- * can be used as initPlans...
+ * Un-correlated or undirect correlated plans of EXISTS, EXPR, or
+ * MULTIEXPR types can be used as initPlans. For EXISTS or EXPR,
+ * we just produce a Param referring to the result of evaluating the
+ * initPlan. For MULTIEXPR, we must build an AND or OR-clause of the
+ * individual comparison operators, using the appropriate lefthand
+ * side expressions and Params for the initPlan's target items.
*/
- if (node->parParam == NULL && slink->subLinkType == EXPR_SUBLINK)
+ if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
+ {
+ Var *var = makeVar(0, 0, BOOLOID, -1, 0);
+ Param *prm = makeNode(Param);
+
+ prm->paramkind = PARAM_EXEC;
+ prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
+ prm->paramtype = var->vartype;
+ pfree(var); /* var is only needed for new_param */
+ node->setParam = lappendi(node->setParam, prm->paramid);
+ PlannerInitPlan = lappend(PlannerInitPlan, node);
+ result = (Node *) prm;
+ }
+ else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
+ {
+ TargetEntry *te = lfirst(plan->targetlist);
+ /* need a var node just to pass to new_param()... */
+ Var *var = makeVar(0, 0, te->resdom->restype,
+ te->resdom->restypmod, 0);
+ Param *prm = makeNode(Param);
+
+ prm->paramkind = PARAM_EXEC;
+ prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
+ prm->paramtype = var->vartype;
+ pfree(var); /* var is only needed for new_param */
+ node->setParam = lappendi(node->setParam, prm->paramid);
+ PlannerInitPlan = lappend(PlannerInitPlan, node);
+ result = (Node *) prm;
+ }
+ else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
{
List *newoper = NIL;
int i = 0;
@@ -202,6 +235,7 @@ make_subplan(SubLink *slink)
prm->paramkind = PARAM_EXEC;
prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
prm->paramtype = var->vartype;
+ pfree(var); /* var is only needed for new_param */
Assert(IsA(oper, Oper));
tup = get_operator_tuple(oper->opno);
@@ -219,7 +253,6 @@ make_subplan(SubLink *slink)
(Var *) left,
(Var *) right));
node->setParam = lappendi(node->setParam, prm->paramid);
- pfree(var);
i++;
}
slink->oper = newoper;
@@ -231,19 +264,6 @@ make_subplan(SubLink *slink)
else
result = (Node *) lfirst(newoper);
}
- else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK)
- {
- Var *var = makeVar(0, 0, BOOLOID, -1, 0);
- Param *prm = makeNode(Param);
-
- prm->paramkind = PARAM_EXEC;
- prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
- prm->paramtype = var->vartype;
- node->setParam = lappendi(node->setParam, prm->paramid);
- pfree(var);
- PlannerInitPlan = lappend(PlannerInitPlan, node);
- result = (Node *) prm;
- }
else
{
/* make expression of SUBPLAN type */
@@ -333,7 +353,8 @@ set_unioni(List *l1, List *l2)
/*
* finalize_primnode: build lists of subplans and params appearing
- * in the given expression tree.
+ * in the given expression tree. NOTE: items are added to lists passed in,
+ * so caller must initialize lists to NIL before first call!
*/
typedef struct finalize_primnode_results {
@@ -341,20 +362,8 @@ typedef struct finalize_primnode_results {
List *paramids; /* List of PARAM_EXEC paramids found */
} finalize_primnode_results;
-static bool finalize_primnode_walker(Node *node,
- finalize_primnode_results *results);
-
-static void
-finalize_primnode(Node *expr, finalize_primnode_results *results)
-{
- results->subplans = NIL; /* initialize */
- results->paramids = NIL;
- (void) finalize_primnode_walker(expr, results);
-}
-
static bool
-finalize_primnode_walker(Node *node,
- finalize_primnode_results *results)
+finalize_primnode(Node *node, finalize_primnode_results *results)
{
if (node == NULL)
return false;
@@ -389,7 +398,7 @@ finalize_primnode_walker(Node *node,
}
/* fall through to recurse into subplan args */
}
- return expression_tree_walker(node, finalize_primnode_walker,
+ return expression_tree_walker(node, finalize_primnode,
(void *) results);
}
@@ -443,7 +452,7 @@ process_sublinks_mutator(Node *node, void *context)
{
SubLink *sublink = (SubLink *) node;
- /* First, scan the lefthand-side expressions.
+ /* First, scan the lefthand-side expressions, if any.
* This is a tad klugy since we modify the input SubLink node,
* but that should be OK (make_subplan does it too!)
*/
@@ -475,23 +484,28 @@ SS_finalize_plan(Plan *plan)
List *lst;
if (plan == NULL)
- return NULL;
+ return NIL;
- /* Find params in targetlist, make sure there are no subplans there */
+ results.subplans = NIL; /* initialize lists to NIL */
+ results.paramids = NIL;
+ /*
+ * When we call finalize_primnode, results.paramids lists are
+ * automatically merged together. But when recursing to self,
+ * we have to do it the hard way. We want the paramids list
+ * to include params in subplans as well as at this level.
+ * (We don't care about finding subplans of subplans, though.)
+ */
+
+ /* Find params and subplans in targetlist and qual */
finalize_primnode((Node *) plan->targetlist, &results);
- Assert(results.subplans == NIL);
+ finalize_primnode((Node *) plan->qual, &results);
- /* From here on, we invoke finalize_primnode_walker not finalize_primnode,
- * so that results.paramids lists are automatically merged together and
- * we don't have to do it the hard way. But when recursing to self,
- * we do have to merge the lists. Oh well.
- */
+ /* Check additional node-type-specific fields */
switch (nodeTag(plan))
{
case T_Result:
- finalize_primnode_walker(((Result *) plan)->resconstantqual,
- &results);
- /* results.subplans is NOT necessarily empty here ... */
+ finalize_primnode(((Result *) plan)->resconstantqual,
+ &results);
break;
case T_Append:
@@ -501,33 +515,26 @@ SS_finalize_plan(Plan *plan)
break;
case T_IndexScan:
- finalize_primnode_walker((Node *) ((IndexScan *) plan)->indxqual,
- &results);
- Assert(results.subplans == NIL);
+ finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
+ &results);
break;
case T_MergeJoin:
- finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses,
- &results);
- Assert(results.subplans == NIL);
+ finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
+ &results);
break;
case T_HashJoin:
- finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses,
- &results);
- Assert(results.subplans == NIL);
+ finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
+ &results);
break;
case T_Hash:
- finalize_primnode_walker((Node *) ((Hash *) plan)->hashkey,
- &results);
- Assert(results.subplans == NIL);
+ finalize_primnode((Node *) ((Hash *) plan)->hashkey,
+ &results);
break;
case T_Agg:
- /* XXX Code used to reject subplans in Aggref args; needed?? */
- break;
-
case T_SeqScan:
case T_NestLoop:
case T_Material:
@@ -539,12 +546,9 @@ SS_finalize_plan(Plan *plan)
default:
elog(ERROR, "SS_finalize_plan: node %d unsupported",
nodeTag(plan));
- return NULL;
}
- finalize_primnode_walker((Node *) plan->qual, &results);
- /* subplans are OK in the qual... */
-
+ /* Process left and right subplans, if any */
results.paramids = set_unioni(results.paramids,
SS_finalize_plan(plan->lefttree));
results.paramids = set_unioni(results.paramids,