diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 1999-11-15 02:00:15 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 1999-11-15 02:00:15 +0000 |
commit | f68e11f373de8b7b1d19203b8edac1a13a8d406d (patch) | |
tree | c4e61de2b9bd9b8dd7c70545dc861de3547294d4 /src/backend/optimizer | |
parent | 1ecb129d206f30f5813b01e1f1efe75c06febe49 (diff) | |
download | postgresql-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.c | 18 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 12 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 132 |
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, |