From f68e11f373de8b7b1d19203b8edac1a13a8d406d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 15 Nov 1999 02:00:15 +0000 Subject: [PATCH] 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. --- src/backend/executor/nodeSubplan.c | 70 +++-- src/backend/nodes/copyfuncs.c | 14 +- src/backend/optimizer/plan/planmain.c | 18 +- src/backend/optimizer/plan/planner.c | 12 +- src/backend/optimizer/plan/subselect.c | 134 ++++---- src/backend/parser/analyze.c | 7 +- src/backend/parser/gram.y | 405 ++++--------------------- src/backend/parser/parse_expr.c | 45 ++- src/backend/rewrite/rewriteHandler.c | 154 +++++----- src/backend/utils/adt/ruleutils.c | 30 +- src/include/nodes/plannodes.h | 6 +- src/include/nodes/primnodes.h | 35 ++- 12 files changed, 364 insertions(+), 566 deletions(-) diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 452e3414b3..dbb824fe64 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -3,6 +3,11 @@ * nodeSubplan.c * routines to support subselects * + * Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.16 1999/11/15 02:00:01 tgl Exp $ + * *------------------------------------------------------------------------- */ /* @@ -68,19 +73,17 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) * within a tuple (if there are multiple columns) using OR semantics * if "useor" is true, AND semantics if not. We then combine results * across tuples (if the subplan produces more than one) using OR - * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK. NULL - * results from the combining operators are handled according to the - * usual SQL semantics for OR and AND. The result for no input - * tuples is FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK. + * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK. + * (MULTIEXPR_SUBLINK doesn't allow multiple tuples from the subplan.) + * NULL results from the combining operators are handled according to + * the usual SQL semantics for OR and AND. The result for no input + * tuples is FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK, NULL for + * MULTIEXPR_SUBLINK. * * For EXPR_SUBLINK we require the subplan to produce no more than one * tuple, else an error is raised. If zero tuples are produced, we - * return NULL. (XXX it would probably be more correct to evaluate - * the combining operator with a NULL input?) Assuming we get a tuple: - * if there is only one column then we just return its result as-is, NULL - * or otherwise. If there is more than one column we combine the results - * per "useor" --- this only makes sense if the combining operators yield - * boolean, and we assume the parser has checked that. + * return NULL. Assuming we get a tuple, we just return its first + * column (there can be only one non-junk column in this case). */ result = (Datum) (subLinkType == ALL_SUBLINK ? true : false); *isNull = false; @@ -98,13 +101,29 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) if (subLinkType == EXISTS_SUBLINK) return (Datum) true; - /* cannot allow multiple input tuples for EXPR sublink */ - if (subLinkType == EXPR_SUBLINK && found) + if (subLinkType == EXPR_SUBLINK) + { + /* cannot allow multiple input tuples for EXPR sublink */ + if (found) + elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect"); + found = true; + /* XXX need to copy tuple in case pass by ref */ + /* XXX need to ref-count the tuple to avoid mem leak! */ + tup = heap_copytuple(tup); + result = heap_getattr(tup, col, tdesc, isNull); + /* keep scanning subplan to make sure there's only one tuple */ + continue; + } + + /* cannot allow multiple input tuples for MULTIEXPR sublink either */ + if (subLinkType == MULTIEXPR_SUBLINK && found) elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect"); found = true; - /* iterate over combining operators for columns of tuple */ + /* For ALL, ANY, and MULTIEXPR sublinks, iterate over combining + * operators for columns of tuple. + */ foreach(lst, sublink->oper) { Expr *expr = (Expr *) lfirst(lst); @@ -193,7 +212,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) } else { - /* must be EXPR_SUBLINK */ + /* must be MULTIEXPR_SUBLINK */ result = rowresult; *isNull = rownull; } @@ -202,9 +221,10 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) if (!found) { /* deal with empty subplan result. result/isNull were previously - * initialized correctly for all sublink types except EXPR. + * initialized correctly for all sublink types except EXPR and + * MULTIEXPR; for those, return NULL. */ - if (subLinkType == EXPR_SUBLINK) + if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK) { result = (Datum) false; *isNull = true; @@ -242,7 +262,7 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent) * If this plan is un-correlated or undirect correlated one and want * to set params for parent plan then prepare parameters. */ - if (node->setParam != NULL) + if (node->setParam != NIL) { List *lst; @@ -293,14 +313,6 @@ ExecSetParamPlan(SubPlan *node) TupleDesc tdesc = slot->ttc_tupleDescriptor; int i = 1; - if (sublink->subLinkType == EXPR_SUBLINK && found) - { - elog(ERROR, "ExecSetParamPlan: more than one tuple returned by expression subselect"); - return; - } - - found = true; - if (sublink->subLinkType == EXISTS_SUBLINK) { ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]); @@ -308,9 +320,17 @@ ExecSetParamPlan(SubPlan *node) prm->execPlan = NULL; prm->value = (Datum) true; prm->isnull = false; + found = true; break; } + if (found && + (sublink->subLinkType == EXPR_SUBLINK || + sublink->subLinkType == MULTIEXPR_SUBLINK)) + elog(ERROR, "ExecSetParamPlan: more than one tuple returned by expression subselect"); + + found = true; + /* * If this is uncorrelated subquery then its plan will be closed * (see below) and this tuple will be free-ed - bad for not byval diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 5b7437b564..d353df58bd 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.94 1999/11/01 05:15:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.95 1999/11/15 02:00:01 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -86,10 +86,11 @@ CopyPlanFields(Plan *from, Plan *newnode) newnode->locParam = listCopy(from->locParam); newnode->chgParam = listCopy(from->chgParam); Node_Copy(from, newnode, initPlan); - if (from->subPlan != NULL) - newnode->subPlan = SS_pull_subplan((Node *) newnode->qual); + if (from->subPlan != NIL) + newnode->subPlan = nconc(SS_pull_subplan((Node *) newnode->targetlist), + SS_pull_subplan((Node *) newnode->qual)); else - newnode->subPlan = NULL; + newnode->subPlan = NIL; newnode->nParamExec = from->nParamExec; } @@ -137,8 +138,9 @@ _copyResult(Result *from) * We must add subplans in resconstantqual to the new plan's subPlan * list */ - newnode->plan.subPlan = nconc(newnode->plan.subPlan, - SS_pull_subplan(newnode->resconstantqual)); + if (from->plan.subPlan != NIL) + newnode->plan.subPlan = nconc(newnode->plan.subPlan, + SS_pull_subplan(newnode->resconstantqual)); return newnode; } diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 3b289fb3d0..7da2ee4c21 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 fce3800dc4..295d722b6a 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 7e5d2be749..5290c96d5d 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 */ - finalize_primnode((Node *) plan->targetlist, &results); - Assert(results.subplans == NIL); - - /* 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. + 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); + finalize_primnode((Node *) plan->qual, &results); + + /* 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, diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index b83e137296..4ad72439b3 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.123 1999/11/07 23:08:10 momjian Exp $ + * $Id: analyze.c,v 1.124 1999/11/15 02:00:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -231,11 +231,11 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* fix where clause */ qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL); - qry->hasSubLinks = pstate->p_hasSubLinks; qry->rtable = pstate->p_rtable; qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL); + qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); @@ -423,6 +423,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) if (stmt->forUpdate != NULL) transformForUpdate(qry, stmt->forUpdate); + /* in case of subselects in default clauses... */ + qry->hasSubLinks = pstate->p_hasSubLinks; + return (Query *) qry; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ba16de5e44..9aa9f93cd1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.113 1999/10/29 23:52:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.114 1999/11/15 02:00:10 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -53,11 +53,12 @@ #include "mb/pg_wchar.h" #endif +extern List *parsetree; /* final parse result is delivered here */ + static char saved_relname[NAMEDATALEN]; /* need this for complex attributes */ static bool QueryIsRule = FALSE; static Oid *param_type_info; static int pfunc_num_args; -extern List *parsetree; /* @@ -75,8 +76,6 @@ static void mapTargetColumns(List *source, List *target); static void param_type_init(Oid *typev, int nargs); static Node *doNegate(Node *n); -Oid param_type(int t); /* used in parse_expr.c */ - /* old versions of flex define this as a macro */ #if defined(yywrap) #undef yywrap @@ -178,7 +177,7 @@ Oid param_type(int t); /* used in parse_expr.c */ %type TriggerForOpt, TriggerForType, OptTemp, OptTempType, OptTempScope %type for_update_clause, update_list -%type opt_union +%type opt_all %type opt_table %type opt_trans @@ -1097,11 +1096,18 @@ OptInherit: INHERITS '(' relation_name_list ')' { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } ; -CreateAsStmt: CREATE OptTemp TABLE relation_name OptCreateAs AS SubSelect +/* + * Note: CREATE TABLE ... AS SELECT ... is just another spelling for + * SELECT ... INTO. + */ + +CreateAsStmt: CREATE OptTemp TABLE relation_name OptCreateAs AS SelectStmt { SelectStmt *n = (SelectStmt *)$7; if ($5 != NIL) mapTargetColumns($5, n->targetList); + if (n->into != NULL) + elog(ERROR,"CREATE TABLE/AS SELECT may not specify INTO"); n->istemp = $2; n->into = $4; $$ = (Node *)n; @@ -2861,7 +2867,7 @@ select_clause: '(' select_clause ')' $$ = (Node *)makeA_Expr(AND,NULL,$1, makeA_Expr(NOT,NULL,NULL,$3)); } - | select_clause UNION opt_union select_clause + | select_clause UNION opt_all select_clause { if (IsA($4, SelectStmt)) { @@ -2919,7 +2925,7 @@ opt_table: TABLE { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; -opt_union: ALL { $$ = TRUE; } +opt_all: ALL { $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } ; @@ -3590,16 +3596,13 @@ a_expr_or_null: a_expr * Define row_descriptor to allow yacc to break the reduce/reduce conflict * with singleton expressions. * Eliminated lots of code by defining row_op and sub_type clauses. - * However, can not consolidate EXPR_LINK case with others subselects - * due to shift/reduce conflict with the non-subselect clause (the parser - * would have to look ahead more than one token to resolve the conflict). * - thomas 1998-05-09 */ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = $2; - n->oper = lcons("=",NIL); + n->oper = lcons("=", NIL); n->useor = false; n->subLinkType = ANY_SUBLINK; n->subselect = $6; @@ -3609,7 +3612,7 @@ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')' { SubLink *n = makeNode(SubLink); n->lefthand = $2; - n->oper = lcons("<>",NIL); + n->oper = lcons("<>", NIL); n->useor = true; n->subLinkType = ALL_SUBLINK; n->subselect = $7; @@ -3637,7 +3640,7 @@ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')' n->useor = true; else n->useor = false; - n->subLinkType = EXPR_SUBLINK; + n->subLinkType = MULTIEXPR_SUBLINK; n->subselect = $6; $$ = (Node *)n; } @@ -3933,16 +3936,6 @@ a_expr: attr n->args = NIL; $$ = (Node *)n; } - | EXISTS '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = NIL; - n->useor = false; - n->oper = NIL; - n->subLinkType = EXISTS_SUBLINK; - n->subselect = $3; - $$ = (Node *)n; - } | EXTRACT '(' extract_list ')' { FuncCall *n = makeNode(FuncCall); @@ -4065,7 +4058,7 @@ a_expr: attr { SubLink *n = (SubLink *)$4; n->lefthand = lcons($1, NIL); - n->oper = lcons("=",NIL); + n->oper = lcons("=", NIL); n->useor = false; n->subLinkType = ANY_SUBLINK; $$ = (Node *)n; @@ -4092,7 +4085,7 @@ a_expr: attr { SubLink *n = (SubLink *)$5; n->lefthand = lcons($1, NIL); - n->oper = lcons("<>",NIL); + n->oper = lcons("<>", NIL); n->useor = false; n->subLinkType = ALL_SUBLINK; $$ = (Node *)n; @@ -4112,334 +4105,34 @@ a_expr: attr $$ = n; } } - | a_expr Op '(' SubSelect ')' + | a_expr row_op sub_type '(' SubSelect ')' { SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons($2,NIL); + n->lefthand = lcons($1, NIL); + n->oper = lcons($2, NIL); + n->useor = false; /* doesn't matter since only one col */ + n->subLinkType = $3; + n->subselect = $5; + $$ = (Node *)n; + } + | EXISTS '(' SubSelect ')' + { + SubLink *n = makeNode(SubLink); + n->lefthand = NIL; + n->oper = NIL; + n->useor = false; + n->subLinkType = EXISTS_SUBLINK; + n->subselect = $3; + $$ = (Node *)n; + } + | '(' SubSelect ')' + { + SubLink *n = makeNode(SubLink); + n->lefthand = NIL; + n->oper = NIL; n->useor = false; n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '+' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("+",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '-' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("-",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '*' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("*",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '/' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("/",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '%' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("%",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '^' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("^",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '|' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("|",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '<' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("<",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '>' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons(">",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr '=' '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("=",NIL); - n->useor = false; - n->subLinkType = EXPR_SUBLINK; - n->subselect = $4; - $$ = (Node *)n; - } - | a_expr Op ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons($2,NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '+' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("+",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '-' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("-",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '*' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("*",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '/' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("/",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '%' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("%",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '^' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("^",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '|' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("|",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '<' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("<",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '>' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons(">",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '=' ANY '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1,NIL); - n->oper = lcons("=",NIL); - n->useor = false; - n->subLinkType = ANY_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr Op ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons($2,NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '+' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("+",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '-' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("-",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '*' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("*",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '/' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("/",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '%' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("%",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '^' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("^",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '|' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("|",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '<' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("<",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '>' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons(">",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; - $$ = (Node *)n; - } - | a_expr '=' ALL '(' SubSelect ')' - { - SubLink *n = makeNode(SubLink); - n->lefthand = lcons($1, NULL); - n->oper = lcons("=",NIL); - n->useor = false; - n->subLinkType = ALL_SUBLINK; - n->subselect = $5; + n->subselect = $2; $$ = (Node *)n; } | a_expr AND a_expr @@ -4702,6 +4395,16 @@ b_expr: attr n->args = $3; $$ = (Node *)n; } + | '(' SubSelect ')' + { + SubLink *n = makeNode(SubLink); + n->lefthand = NIL; + n->oper = NIL; + n->useor = false; + n->subLinkType = EXPR_SUBLINK; + n->subselect = $2; + $$ = (Node *)n; + } ; opt_indirection: '[' a_expr ']' opt_indirection @@ -5345,8 +5048,6 @@ mapTargetColumns(List *src, List *dst) src = lnext(src); dst = lnext(dst); } - - return; } /* mapTargetColumns() */ diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index ee9d307728..76e5f91f1a 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.58 1999/09/13 04:14:56 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.59 1999/11/15 02:00:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -220,8 +220,30 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) sublink->lefthand = NIL; sublink->oper = NIL; } + else if (sublink->subLinkType == EXPR_SUBLINK) + { + List *tlist = qtree->targetList; + + /* Make sure the subselect delivers a single column + * (ignoring resjunk targets). + */ + if (tlist == NIL || + ((TargetEntry *) lfirst(tlist))->resdom->resjunk) + elog(ERROR, "parser: subselect must have a field"); + while ((tlist = lnext(tlist)) != NIL) + { + if (! ((TargetEntry *) lfirst(tlist))->resdom->resjunk) + elog(ERROR, "parser: subselect must have only one field"); + } + /* EXPR needs no lefthand or combining operator. + * These fields should be NIL already, but make sure. + */ + sublink->lefthand = NIL; + sublink->oper = NIL; + } else { + /* ALL, ANY, or MULTIEXPR: generate operator list */ char *op = lfirst(sublink->oper); List *left_list = sublink->lefthand; List *right_list = qtree->targetList; @@ -231,9 +253,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) lfirst(elist) = transformExpr(pstate, lfirst(elist), precedence); - if (length(left_list) > 1 && + /* Combining operators other than =/<> is dubious... */ + if (length(left_list) != 1 && strcmp(op, "=") != 0 && strcmp(op, "<>") != 0) - elog(ERROR, "parser: '%s' is not relational operator", + elog(ERROR, "parser: '%s' is not usable for row comparison", op); sublink->oper = NIL; @@ -266,8 +289,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) FALSE); opform = (Form_pg_operator) GETSTRUCT(optup); - if (opform->oprresult != BOOLOID && - sublink->subLinkType != EXPR_SUBLINK) + if (opform->oprresult != BOOLOID) elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op); newop = makeOper(oprid(optup),/* opno */ @@ -589,13 +611,14 @@ exprType(Node *expr) if (sublink->subLinkType == EXPR_SUBLINK) { - /* return the result type of the combining operator; - * should only be one... - */ - Oper *op = (Oper *) lfirst(sublink->oper); + /* get the type of the subselect's first target column */ + Query *qtree = (Query *) sublink->subselect; + TargetEntry *tent; - Assert(IsA(op, Oper)); - type = op->opresulttype; + if (! qtree || ! IsA(qtree, Query)) + elog(ERROR, "exprType: can't get type for untransformed sublink"); + tent = (TargetEntry *) lfirst(qtree->targetList); + type = tent->resdom->restype; } else { diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 1051ddb146..0f3dbba136 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.62 1999/11/01 05:18:31 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.63 1999/11/15 02:00:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,9 +56,9 @@ static bool attribute_used(Node *node, int rt_index, int attno, int sublevels_up); static bool modifyAggrefUplevel(Node *node, void *context); static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index, - int sublevels_up); + int sublevels_up, int new_sublevels_up); static Node *modifyAggrefDropQual(Node *node, Node *targetNode); -static SubLink *modifyAggrefMakeSublink(Expr *origexp, Query *parsetree); +static SubLink *modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree); static Node *modifyAggrefQual(Node *node, Query *parsetree); static bool checkQueryHasAggs(Node *node); static bool checkQueryHasAggs_walker(Node *node, void *context); @@ -357,6 +357,7 @@ typedef struct { int rt_index; int new_index; int sublevels_up; + int new_sublevels_up; } modifyAggrefChangeVarnodes_context; static bool @@ -374,7 +375,7 @@ modifyAggrefChangeVarnodes_walker(Node *node, { var->varno = context->new_index; var->varnoold = context->new_index; - var->varlevelsup = 0; + var->varlevelsup = context->new_sublevels_up; } return false; } @@ -392,7 +393,8 @@ modifyAggrefChangeVarnodes_walker(Node *node, if (modifyAggrefChangeVarnodes((Node *) (sub->subselect), context->rt_index, context->new_index, - context->sublevels_up + 1)) + context->sublevels_up + 1, + context->new_sublevels_up + 1)) return true; return false; } @@ -418,13 +420,14 @@ modifyAggrefChangeVarnodes_walker(Node *node, static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index, - int sublevels_up) + int sublevels_up, int new_sublevels_up) { modifyAggrefChangeVarnodes_context context; context.rt_index = rt_index; context.new_index = new_index; context.sublevels_up = sublevels_up; + context.new_sublevels_up = new_sublevels_up; return modifyAggrefChangeVarnodes_walker(node, &context); } @@ -433,8 +436,13 @@ modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index, * modifyAggrefDropQual - * remove the pure aggref clause from a qualification * - * targetNode is a boolean expression node somewhere within the given - * expression tree. When we find it, replace it with a constant TRUE. + * targetNode is an Aggref node somewhere within the given expression tree. + * Find the boolean operator that's presumably somewhere above it, and replace + * that whole operator expression with a constant TRUE. (This is NOT really + * quite the right thing, but it handles simple cases. This whole set of + * Aggref-in-qual routines needs to be thrown away when we can do subselects + * in FROM.) + * * The return tree is a modified copy of the given tree; the given tree * is not altered. * @@ -448,14 +456,28 @@ modifyAggrefDropQual(Node *node, Node *targetNode) if (node == NULL) return NULL; if (node == targetNode) + { + /* Oops, it's not inside an Expr we can rearrange... */ + elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause"); + } + if (IsA(node, Expr)) { Expr *expr = (Expr *) node; + List *i; - if (! IsA(expr, Expr) || expr->typeOid != BOOLOID) - elog(ERROR, - "aggregate expression in qualification isn't of type bool"); - return (Node *) makeConst(BOOLOID, 1, (Datum) true, - false, true, false, false); + foreach(i, expr->args) + { + if (((Node *) lfirst(i)) == targetNode) + { + /* Found the parent expression containing the Aggref */ + if (expr->typeOid != BOOLOID) + elog(ERROR, + "aggregate function in qual must be argument of boolean operator"); + return (Node *) makeConst(BOOLOID, 1, (Datum) true, + false, true, false, false); + } + } + /* else this isn't the expr we want, keep going */ } return expression_tree_mutator(node, modifyAggrefDropQual, (void *) targetNode); @@ -467,27 +489,17 @@ modifyAggrefDropQual(Node *node, Node *targetNode) * uses an aggregate column of a view */ static SubLink * -modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) +modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree) { - SubLink *sublink; - Query *subquery; - RangeTblEntry *rte; - Aggref *aggref; + /* target and rte point to old structures: */ Var *target; + RangeTblEntry *rte; + /* these point to newly-created structures: */ + Query *subquery; + SubLink *sublink; TargetEntry *tle; Resdom *resdom; - Expr *exp = copyObject(origexp); - if (IsA(nth(0, exp->args), Aggref)) - { - if (IsA(nth(1, exp->args), Aggref)) - elog(ERROR, "rewrite: comparison of 2 aggregate columns not supported"); - else - elog(ERROR, "rewrite: aggregate column of view must be at right side in qual"); - /* XXX could try to commute operator, instead of failing */ - } - - aggref = (Aggref *) nth(1, exp->args); target = (Var *) (aggref->target); if (! IsA(target, Var)) elog(ERROR, "rewrite: aggregates of views only allowed on simple variables for now"); @@ -504,16 +516,15 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) tle = makeNode(TargetEntry); tle->resdom = resdom; - tle->expr = (Node *) aggref; /* note this is from the copied expr */ + tle->expr = copyObject(aggref); /* make a modifiable copy! */ + + subquery = makeNode(Query); sublink = makeNode(SubLink); sublink->subLinkType = EXPR_SUBLINK; sublink->useor = false; - /* note lefthand and oper are made from the copied expr */ - sublink->lefthand = lcons(lfirst(exp->args), NIL); - sublink->oper = lcons(exp->oper, NIL); - - subquery = makeNode(Query); + sublink->lefthand = NIL; + sublink->oper = NIL; sublink->subselect = (Node *) subquery; subquery->commandType = CMD_SELECT; @@ -526,33 +537,31 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) subquery->unionall = FALSE; subquery->uniqueFlag = NULL; subquery->sortClause = NULL; - subquery->rtable = lcons(rte, NIL); + subquery->rtable = lcons(copyObject(rte), NIL); subquery->targetList = lcons(tle, NIL); subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual, - (Node *) origexp); + (Node *) aggref); /* * If there are still aggs in the subselect's qual, give up. * Recursing would be a bad idea --- we'd likely produce an * infinite recursion. This whole technique is a crock, really... */ if (checkQueryHasAggs(subquery->qual)) - elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause"); + elog(ERROR, "Cannot handle multiple aggregate functions in WHERE clause"); subquery->groupClause = NIL; subquery->havingQual = NULL; subquery->hasAggs = TRUE; - subquery->hasSubLinks = FALSE; + subquery->hasSubLinks = checkQueryHasSubLink(subquery->qual); subquery->unionClause = NULL; + /* Increment all varlevelsup fields in the new subquery */ modifyAggrefUplevel((Node *) subquery, NULL); - /* - * Note: it might appear that we should be passing target->varlevelsup+1 - * here, since modifyAggrefUplevel has increased all the varlevelsup - * values in the subquery. However, target itself is a pointer to a - * Var node in the subquery, so it's been incremented too! What a kluge - * this all is ... we need to make subquery RTEs so it can go away... + + /* Replace references to the target table with correct varno. + * Note +1 here to account for effects of previous line! */ modifyAggrefChangeVarnodes((Node *) subquery, target->varno, - 1, target->varlevelsup); + 1, target->varlevelsup+1, 0); return sublink; } @@ -572,30 +581,17 @@ modifyAggrefQual(Node *node, Query *parsetree) { if (node == NULL) return NULL; - if (IsA(node, Expr)) - { - Expr *expr = (Expr *) node; - - if (length(expr->args) == 2 && - (IsA(lfirst(expr->args), Aggref) || - IsA(lsecond(expr->args), Aggref))) - { - SubLink *sub = modifyAggrefMakeSublink(expr, - parsetree); - parsetree->hasSubLinks = true; - /* check for aggs in resulting lefthand... */ - sub->lefthand = (List *) modifyAggrefQual((Node *) sub->lefthand, - parsetree); - return (Node *) sub; - } - /* otherwise, fall through and copy the expr normally */ - } if (IsA(node, Aggref)) { - /* Oops, found one that's not inside an Expr we can rearrange... */ - elog(ERROR, "Cannot handle aggregate function inserted at this place in WHERE clause"); + SubLink *sub = modifyAggrefMakeSublink((Aggref *) node, parsetree); + + parsetree->hasSubLinks = true; + return (Node *) sub; } - /* We do NOT recurse into subselects in this routine. It's sufficient + /* + * Otherwise, fall through and copy the expr normally. + * + * We do NOT recurse into subselects in this routine. It's sufficient * to get rid of aggregates that are in the qual expression proper. */ return expression_tree_mutator(node, modifyAggrefQual, @@ -1113,7 +1109,8 @@ fireRIRrules(Query *parsetree) if (parsetree->hasSubLinks) fireRIRonSubselect((Node *) parsetree, NULL); - parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree); + if (parsetree->hasAggs) + parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree); return parsetree; } @@ -1577,17 +1574,22 @@ BasicQueryRewrite(Query *parsetree) /* * If the query was marked having aggregates, check if this is - * still true after rewriting. Ditto for sublinks. - * - * This check must get expanded when someday aggregates can appear - * somewhere else than in the targetlist or the having qual. + * still true after rewriting. Ditto for sublinks. Note there + * should be no aggs in the qual at this point. */ if (query->hasAggs) - query->hasAggs = checkQueryHasAggs((Node *) (query->targetList)) - || checkQueryHasAggs((Node *) (query->havingQual)); + { + query->hasAggs = + checkQueryHasAggs((Node *) (query->targetList)) || + checkQueryHasAggs((Node *) (query->havingQual)); + if (checkQueryHasAggs((Node *) (query->qual))) + elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual"); + } if (query->hasSubLinks) - query->hasSubLinks = checkQueryHasSubLink((Node *) (query->qual)) - || checkQueryHasSubLink((Node *) (query->havingQual)); + query->hasSubLinks = + checkQueryHasSubLink((Node *) (query->targetList)) || + checkQueryHasSubLink((Node *) (query->qual)) || + checkQueryHasSubLink((Node *) (query->havingQual)); results = lappend(results, query); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 22f904b60d..89febb159b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * out of it's tuple * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.30 1999/11/07 23:08:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.31 1999/11/15 02:00:05 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1681,15 +1681,17 @@ get_sublink_expr(Node *node, deparse_context *context) StringInfo buf = context->buf; SubLink *sublink = (SubLink *) node; Query *query = (Query *) (sublink->subselect); - Oper *oper; List *l; char *sep; + Oper *oper; + bool need_paren; appendStringInfo(buf, "("); - if (sublink->lefthand != NULL) + if (sublink->lefthand != NIL) { - if (length(sublink->lefthand) > 1) + need_paren = (length(sublink->lefthand) > 1); + if (need_paren) appendStringInfo(buf, "("); sep = ""; @@ -1700,12 +1702,14 @@ get_sublink_expr(Node *node, deparse_context *context) get_rule_expr((Node *) lfirst(l), context); } - if (length(sublink->lefthand) > 1) + if (need_paren) appendStringInfo(buf, ") "); else appendStringInfo(buf, " "); } + need_paren = true; + switch (sublink->subLinkType) { case EXISTS_SUBLINK: @@ -1722,20 +1726,30 @@ get_sublink_expr(Node *node, deparse_context *context) appendStringInfo(buf, "%s ALL ", get_opname(oper->opno)); break; - case EXPR_SUBLINK: + case MULTIEXPR_SUBLINK: oper = (Oper *) lfirst(sublink->oper); appendStringInfo(buf, "%s ", get_opname(oper->opno)); break; + case EXPR_SUBLINK: + need_paren = false; + break; + default: elog(ERROR, "get_sublink_expr: unsupported sublink type %d", sublink->subLinkType); break; } - appendStringInfo(buf, "("); + if (need_paren) + appendStringInfo(buf, "("); + get_query_def(query, buf, context->rangetables); - appendStringInfo(buf, "))"); + + if (need_paren) + appendStringInfo(buf, "))"); + else + appendStringInfo(buf, ")"); } /* ---------- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index a03dacfb02..e4dc5fde21 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: plannodes.h,v 1.31 1999/10/17 22:15:07 tgl Exp $ + * $Id: plannodes.h,v 1.32 1999/11/15 02:00:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -356,8 +356,8 @@ typedef struct SubPlan List *setParam; /* non-correlated EXPR & EXISTS subqueries * have to set some Params for paren Plan */ List *parParam; /* indices of corr. Vars from parent plan */ - SubLink *sublink; /* SubLink node for subselects in WHERE - * and HAVING */ + SubLink *sublink; /* SubLink node from parser; holds info about + * what to do with subselect's results */ bool shutdown; /* shutdown plan if TRUE */ } SubPlan; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index e2ef42218f..2d585bdcc5 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.36 1999/08/25 23:21:36 tgl Exp $ + * $Id: primnodes.h,v 1.37 1999/11/15 02:00:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -98,8 +98,7 @@ typedef struct Fjoin * Expr * typeOid - oid of the type of this expression * opType - type of this expression - * oper - the Oper node if it is an OPER_EXPR or the - * Func node if it is a FUNC_EXPR + * oper - operator node if needed (Oper, Func, or SubPlan) * args - arguments to this expression * ---------------- */ @@ -318,12 +317,31 @@ typedef struct Aggref /* ---------------- * SubLink - * subLinkType - EXISTS, ALL, ANY, EXPR - * useor - TRUE for <> (combine op results with "or" not "and") + * subLinkType - EXISTS, ALL, ANY, MULTIEXPR, EXPR + * useor - TRUE to combine column results with "OR" not "AND" * lefthand - list of outer-query expressions on the left - * oper - list of Oper nodes + * oper - list of Oper nodes for combining operators * subselect - subselect as Query* or parsetree * + * A SubLink represents a subselect appearing in an expression, and in some + * cases also the combining operator(s) just above it. The subLinkType + * indicates the form of the expression represented: + * EXISTS_SUBLINK EXISTS(SELECT ...) + * ALL_SUBLINK (lefthand) op ALL (SELECT ...) + * ANY_SUBLINK (lefthand) op ANY (SELECT ...) + * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...) + * EXPR_SUBLINK (SELECT with single targetlist item ...) + * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the + * same length as the subselect's targetlist. MULTIEXPR will *always* have + * a list with more than one entry; if the subselect has just one target + * then the parser will create an EXPR_SUBLINK instead (and any operator + * above the subselect will be represented separately). Note that both + * MULTIEXPR and EXPR require the subselect to deliver only one row. + * ALL, ANY, and MULTIEXPR require the combining operators to deliver boolean + * results. These are reduced to one result per row using OR or AND semantics + * depending on the "useor" flag. ALL and ANY combine the per-row results + * using AND and OR semantics respectively. + * * NOTE: lefthand and oper have varying meanings depending on where you look * in the parse/plan pipeline: * 1. gram.y delivers a list of the (untransformed) lefthand expressions in @@ -348,12 +366,13 @@ typedef struct Aggref * representation 2 appears in a "bare" SubLink, while representation 3 is * found in SubLinks that are children of SubPlan nodes. * - * In an EXISTS SubLink, both lefthand and oper are unused and are always NIL. + * In EXISTS and EXPR SubLinks, both lefthand and oper are unused and are + * always NIL. useor is not significant either for these sublink types. * ---------------- */ typedef enum SubLinkType { - EXISTS_SUBLINK, ALL_SUBLINK, ANY_SUBLINK, EXPR_SUBLINK + EXISTS_SUBLINK, ALL_SUBLINK, ANY_SUBLINK, MULTIEXPR_SUBLINK, EXPR_SUBLINK } SubLinkType;