From 42af56e1ead3306d2c056ff96ea770e4eee68e9d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 25 Aug 1999 23:21:43 +0000 Subject: [PATCH] Revise implementation of SubLinks so that there is a consistent, documented intepretation of the lefthand and oper fields. Fix a number of obscure problems while at it --- for example, the old code failed if the parser decided to insert a type-coercion function just below the operator of a SubLink. CAUTION: this will break stored rules that contain subplans. You may need to initdb. --- src/backend/executor/nodeSubplan.c | 19 +- src/backend/optimizer/plan/subselect.c | 315 ++++++++++++------------- src/backend/optimizer/util/clauses.c | 53 +++-- src/backend/parser/parse_expr.c | 66 ++++-- src/backend/rewrite/rewriteHandler.c | 79 +++---- src/backend/rewrite/rewriteManip.c | 65 +---- src/backend/utils/adt/ruleutils.c | 17 +- src/include/nodes/primnodes.h | 32 ++- 8 files changed, 325 insertions(+), 321 deletions(-) diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e4e83d654a..4bd0eb2ff3 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -94,8 +94,25 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext) Const *con = lsecond(expr->args); bool isnull; + /* + * The righthand side of the expression should be either a Const + * or a function call taking a Const as arg (the function would + * be a run-time type coercion inserted by the parser to get to + * the input type needed by the operator). Find the Const node + * and insert the actual righthand side value into it. + */ + if (! IsA(con, Const)) + { + Assert(IsA(con, Expr)); + con = lfirst(((Expr *) con)->args); + Assert(IsA(con, Const)); + } con->constvalue = heap_getattr(tup, i, tdesc, &(con->constisnull)); - result = ExecEvalExpr((Node *) expr, econtext, &isnull, (bool *) NULL); + /* + * Now we can eval the expression. + */ + result = ExecEvalExpr((Node *) expr, econtext, &isnull, + (bool *) NULL); if (isnull) { if (subLinkType == EXPR_SUBLINK) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c275b7adc4..7e5d2be749 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -6,18 +6,24 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.23 1999/08/22 20:14:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.24 1999/08/25 23:21:39 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/planner.h" #include "optimizer/subselect.h" +#include "parser/parse_expr.h" +#include "parser/parse_node.h" +#include "parser/parse_oper.h" +#include "utils/lsyscache.h" + int PlannerQueryLevel; /* level of current query */ List *PlannerInitPlan; /* init subplans for current query */ @@ -46,7 +52,7 @@ int PlannerPlanId; /* to assign unique ID to subquery plans */ * is set from the absolute level value given by varlevel. */ static int -_new_param(Var *var, int varlevel) +new_param(Var *var, int varlevel) { Var *paramVar = (Var *) copyObject(var); @@ -62,7 +68,7 @@ _new_param(Var *var, int varlevel) * which is expected to have varlevelsup > 0 (ie, it is not local). */ static Param * -_replace_var(Var *var) +replace_var(Var *var) { List *ppv; Param *retval; @@ -98,7 +104,7 @@ _replace_var(Var *var) if (! ppv) { /* Nope, so make a new one */ - i = _new_param(var, varlevel); + i = new_param(var, varlevel); } retval = makeNode(Param); @@ -109,8 +115,11 @@ _replace_var(Var *var) return retval; } +/* + * Convert a bare SubLink (as created by the parser) into a SubPlan. + */ static Node * -_make_subplan(SubLink *slink) +make_subplan(SubLink *slink) { SubPlan *node = makeNode(SubPlan); Plan *plan; @@ -126,7 +135,7 @@ _make_subplan(SubLink *slink) /* * Assign subPlan, extParam and locParam to plan nodes. At the moment, - * SS_finalize_plan doesn't handle initPlan-s and so we assigne them + * SS_finalize_plan doesn't handle initPlan-s and so we assign them * to the topmost plan node and take care about its extParam too. */ (void) SS_finalize_plan(plan); @@ -169,31 +178,58 @@ _make_subplan(SubLink *slink) */ if (node->parParam == NULL && slink->subLinkType == EXPR_SUBLINK) { + List *newoper = NIL; int i = 0; - /* transform right side of all sublink Oper-s into Param */ + /* + * Convert oper list of Opers into a list of Exprs, using + * lefthand arguments and Params representing inside results. + */ foreach(lst, slink->oper) { - List *rside = lnext(((Expr *) lfirst(lst))->args); + Oper *oper = (Oper *) lfirst(lst); + Node *lefthand = nth(i, slink->lefthand); TargetEntry *te = nth(i, 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); + Operator tup; + Form_pg_operator opform; + Node *left, + *right; prm->paramkind = PARAM_EXEC; - prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel); + prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel); prm->paramtype = var->vartype; - lfirst(rside) = prm; + + Assert(IsA(oper, Oper)); + tup = get_operator_tuple(oper->opno); + Assert(HeapTupleIsValid(tup)); + opform = (Form_pg_operator) GETSTRUCT(tup); + /* Note: we use make_operand in case runtime type conversion + * function calls must be inserted for this operator! + */ + left = make_operand("", lefthand, + exprType(lefthand), opform->oprleft); + right = make_operand("", (Node *) prm, + prm->paramtype, opform->oprright); + newoper = lappend(newoper, + make_opclause(oper, + (Var *) left, + (Var *) right)); node->setParam = lappendi(node->setParam, prm->paramid); pfree(var); i++; } + slink->oper = newoper; + slink->lefthand = NIL; PlannerInitPlan = lappend(PlannerInitPlan, node); if (i > 1) - result = (Node *) ((slink->useor) ? make_orclause(slink->oper) : - make_andclause(slink->oper)); + result = (Node *) ((slink->useor) ? make_orclause(newoper) : + make_andclause(newoper)); else - result = (Node *) lfirst(slink->oper); + result = (Node *) lfirst(newoper); } else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK) { @@ -201,7 +237,7 @@ _make_subplan(SubLink *slink) Param *prm = makeNode(Param); prm->paramkind = PARAM_EXEC; - prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel); + prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel); prm->paramtype = var->vartype; node->setParam = lappendi(node->setParam, prm->paramid); pfree(var); @@ -213,16 +249,15 @@ _make_subplan(SubLink *slink) /* make expression of SUBPLAN type */ Expr *expr = makeNode(Expr); List *args = NIL; + List *newoper = NIL; int i = 0; - expr->typeOid = BOOLOID; + expr->typeOid = BOOLOID; /* bogus, but we don't really care */ expr->opType = SUBPLAN_EXPR; expr->oper = (Node *) node; /* - * Make expr->args from parParam. Left sides of sublink Oper-s are - * handled by optimizer directly... Also, transform right side of - * sublink Oper-s into Const. + * Make expr->args from parParam. */ foreach(lst, node->parParam) { @@ -236,23 +271,55 @@ _make_subplan(SubLink *slink) var->varlevelsup = 0; args = lappend(args, var); } + expr->args = args; + /* + * Convert oper list of Opers into a list of Exprs, using + * lefthand arguments and Consts representing inside results. + */ foreach(lst, slink->oper) { - List *rside = lnext(((Expr *) lfirst(lst))->args); + Oper *oper = (Oper *) lfirst(lst); + Node *lefthand = nth(i, slink->lefthand); TargetEntry *te = nth(i, plan->targetlist); - Const *con = makeConst(te->resdom->restype, - 0, 0, true, 0, 0, 0); - - lfirst(rside) = con; + Const *con; + Operator tup; + Form_pg_operator opform; + Node *left, + *right; + + /* + * XXX really ought to fill in constlen and constbyval correctly, + * but right now ExecEvalExpr won't look at them... + */ + con = makeConst(te->resdom->restype, 0, 0, true, 0, 0, 0); + + Assert(IsA(oper, Oper)); + tup = get_operator_tuple(oper->opno); + Assert(HeapTupleIsValid(tup)); + opform = (Form_pg_operator) GETSTRUCT(tup); + /* Note: we use make_operand in case runtime type conversion + * function calls must be inserted for this operator! + */ + left = make_operand("", lefthand, + exprType(lefthand), opform->oprleft); + right = make_operand("", (Node *) con, + con->consttype, opform->oprright); + newoper = lappend(newoper, + make_opclause(oper, + (Var *) left, + (Var *) right)); i++; } - expr->args = args; + slink->oper = newoper; + slink->lefthand = NIL; result = (Node *) expr; } return result; } +/* this oughta be merged with LispUnioni */ + static List * set_unioni(List *l1, List *l2) { @@ -264,6 +331,11 @@ set_unioni(List *l1, List *l2) return nconc(l1, set_differencei(l2, l1)); } +/* + * finalize_primnode: build lists of subplans and params appearing + * in the given expression tree. + */ + typedef struct finalize_primnode_results { List *subplans; /* List of subplans found in expr */ List *paramids; /* List of PARAM_EXEC paramids found */ @@ -315,165 +387,83 @@ finalize_primnode_walker(Node *node, ! intMember(paramid, results->paramids)) results->paramids = lconsi(paramid, results->paramids); } - /* XXX We do NOT allow expression_tree_walker to examine the args - * passed to the subplan. Is that correct??? It's what the - * old code did, but it seems mighty bogus... tgl 7/14/99 - */ - return false; /* don't recurse into subplan args */ + /* fall through to recurse into subplan args */ } return expression_tree_walker(node, finalize_primnode_walker, (void *) results); } -/* Replace correlation vars (uplevel vars) with Params. */ - -/* XXX should replace this with use of a generalized tree rebuilder, - * designed along the same lines as expression_tree_walker. - * Not done yet. +/* + * Replace correlation vars (uplevel vars) with Params. */ + +static Node *replace_correlation_vars_mutator(Node *node, void *context); + Node * SS_replace_correlation_vars(Node *expr) { - if (expr == NULL) - return NULL; - if (IsA(expr, Var)) - { - if (((Var *) expr)->varlevelsup > 0) - expr = (Node *) _replace_var((Var *) expr); - } - else if (single_node(expr)) - return expr; - else if (IsA(expr, List)) - { - List *le; - - foreach(le, (List *) expr) - lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le)); - } - else if (IsA(expr, Expr)) - { - /* XXX do we need to do anything special with subplans? */ - ((Expr *) expr)->args = (List *) - SS_replace_correlation_vars((Node *) ((Expr *) expr)->args); - } - else if (IsA(expr, Aggref)) - ((Aggref *) expr)->target = SS_replace_correlation_vars(((Aggref *) expr)->target); - else if (IsA(expr, Iter)) - ((Iter *) expr)->iterexpr = SS_replace_correlation_vars(((Iter *) expr)->iterexpr); - else if (IsA(expr, ArrayRef)) - { - ((ArrayRef *) expr)->refupperindexpr = (List *) - SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr); - ((ArrayRef *) expr)->reflowerindexpr = (List *) - SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->reflowerindexpr); - ((ArrayRef *) expr)->refexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refexpr); - ((ArrayRef *) expr)->refassgnexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refassgnexpr); - } - else if (IsA(expr, CaseExpr)) - { - CaseExpr *caseexpr = (CaseExpr *) expr; - List *le; + /* No setup needed for tree walk, so away we go */ + return replace_correlation_vars_mutator(expr, NULL); +} - foreach(le, caseexpr->args) - { - CaseWhen *when = (CaseWhen *) lfirst(le); - Assert(IsA(when, CaseWhen)); - when->expr = SS_replace_correlation_vars(when->expr); - when->result = SS_replace_correlation_vars(when->result); - } - /* caseexpr->arg should be null, but we'll check it anyway */ - caseexpr->arg = SS_replace_correlation_vars(caseexpr->arg); - caseexpr->defresult = SS_replace_correlation_vars(caseexpr->defresult); - } - else if (IsA(expr, TargetEntry)) - ((TargetEntry *) expr)->expr = SS_replace_correlation_vars(((TargetEntry *) expr)->expr); - else if (IsA(expr, SubLink)) +static Node * +replace_correlation_vars_mutator(Node *node, void *context) +{ + if (node == NULL) + return NULL; + if (IsA(node, Var)) { - List *le; - - foreach(le, ((SubLink *) expr)->oper) /* left sides only */ - { - List *oparg = ((Expr *) lfirst(le))->args; - - lfirst(oparg) = (List *) - SS_replace_correlation_vars((Node *) lfirst(oparg)); - } - ((SubLink *) expr)->lefthand = (List *) - SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand); + if (((Var *) node)->varlevelsup > 0) + return (Node *) replace_var((Var *) node); } - else - elog(ERROR, "SS_replace_correlation_vars: can't handle node %d", - nodeTag(expr)); - - return expr; + return expression_tree_mutator(node, + replace_correlation_vars_mutator, + context); } -/* Replace sublinks by subplans in the given expression */ - -/* XXX should replace this with use of a generalized tree rebuilder, - * designed along the same lines as expression_tree_walker. - * Not done yet. +/* + * Expand SubLinks to SubPlans in the given expression. */ + +static Node *process_sublinks_mutator(Node *node, void *context); + Node * SS_process_sublinks(Node *expr) { - if (expr == NULL) + /* No setup needed for tree walk, so away we go */ + return process_sublinks_mutator(expr, NULL); +} + +static Node * +process_sublinks_mutator(Node *node, void *context) +{ + if (node == NULL) return NULL; - if (IsA(expr, SubLink)) - { - expr = _make_subplan((SubLink *) expr); - } - else if (single_node(expr)) - return expr; - else if (IsA(expr, List)) + if (IsA(node, SubLink)) { - List *le; + SubLink *sublink = (SubLink *) node; - foreach(le, (List *) expr) - lfirst(le) = SS_process_sublinks((Node *) lfirst(le)); - } - else if (IsA(expr, Expr)) - { - /* We should never see a subplan node here, since this is the - * routine that makes 'em in the first place. No need to check. + /* First, scan the lefthand-side expressions. + * This is a tad klugy since we modify the input SubLink node, + * but that should be OK (make_subplan does it too!) */ - ((Expr *) expr)->args = (List *) - SS_process_sublinks((Node *) ((Expr *) expr)->args); - } - else if (IsA(expr, Aggref)) - ((Aggref *) expr)->target = SS_process_sublinks(((Aggref *) expr)->target); - else if (IsA(expr, Iter)) - ((Iter *) expr)->iterexpr = SS_process_sublinks(((Iter *) expr)->iterexpr); - else if (IsA(expr, ArrayRef)) - { - ((ArrayRef *) expr)->refupperindexpr = (List *) - SS_process_sublinks((Node *) ((ArrayRef *) expr)->refupperindexpr); - ((ArrayRef *) expr)->reflowerindexpr = (List *) - SS_process_sublinks((Node *) ((ArrayRef *) expr)->reflowerindexpr); - ((ArrayRef *) expr)->refexpr = SS_process_sublinks(((ArrayRef *) expr)->refexpr); - ((ArrayRef *) expr)->refassgnexpr = SS_process_sublinks(((ArrayRef *) expr)->refassgnexpr); - } - else if (IsA(expr, CaseExpr)) - { - CaseExpr *caseexpr = (CaseExpr *) expr; - List *le; - - foreach(le, caseexpr->args) - { - CaseWhen *when = (CaseWhen *) lfirst(le); - Assert(IsA(when, CaseWhen)); - when->expr = SS_process_sublinks(when->expr); - when->result = SS_process_sublinks(when->result); - } - /* caseexpr->arg should be null, but we'll check it anyway */ - caseexpr->arg = SS_process_sublinks(caseexpr->arg); - caseexpr->defresult = SS_process_sublinks(caseexpr->defresult); + sublink->lefthand = (List *) + process_sublinks_mutator((Node *) sublink->lefthand, context); + /* Now build the SubPlan node and make the expr to return */ + return make_subplan(sublink); } - else - elog(ERROR, "SS_process_sublinks: can't handle node %d", - nodeTag(expr)); + /* + * Note that we will never see a SubPlan expression in the input + * (since this is the very routine that creates 'em to begin with). + * So the code in expression_tree_mutator() that might do + * inappropriate things with SubPlans or SubLinks will not be + * exercised. + */ + Assert(! is_subplan(node)); - return expr; + return expression_tree_mutator(node, + process_sublinks_mutator, + context); } List * @@ -585,7 +575,9 @@ SS_finalize_plan(Plan *plan) return results.paramids; } -/* Construct a list of all subplans found within the given node tree */ +/* + * Construct a list of all subplans found within the given node tree. + */ static bool SS_pull_subplan_walker(Node *node, List **listptr); @@ -606,8 +598,7 @@ SS_pull_subplan_walker(Node *node, List **listptr) if (is_subplan(node)) { *listptr = lappend(*listptr, ((Expr *) node)->oper); - /* XXX original code did not examine args to subplan, is this right? */ - return false; + /* fall through to check args to subplan */ } return expression_tree_walker(node, SS_pull_subplan_walker, (void *) listptr); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index fbb5a98e83..2d960b5cf0 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.48 1999/08/22 20:14:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.49 1999/08/25 23:21:41 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -817,8 +817,8 @@ CommuteClause(Expr *clause) * the args and slink->oper lists (which belong to the outer plan), but it * will *not* visit the inner plan, since that's typically what expression * tree walkers want. A walker that wants to visit the subplan can force - * appropriate behavior by recognizing subplan nodes and doing the right - * thing. + * appropriate behavior by recognizing subplan expression nodes and doing + * the right thing. * * Bare SubLink nodes (without a SUBPLAN_EXPR) are handled by recursing into * the "lefthand" argument list only. (A bare SubLink should be seen only if @@ -854,26 +854,18 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) case T_Expr: { Expr *expr = (Expr *) node; + if (expr->opType == SUBPLAN_EXPR) { - /* examine args list (params to be passed to subplan) */ - if (expression_tree_walker((Node *) expr->args, - walker, context)) - return true; - /* examine oper list as well */ - if (expression_tree_walker( - (Node *) ((SubPlan *) expr->oper)->sublink->oper, - walker, context)) - return true; - /* but not the subplan itself */ - } - else - { - /* for other Expr node types, just examine args list */ - if (expression_tree_walker((Node *) expr->args, - walker, context)) + /* recurse to the SubLink node (skipping SubPlan!) */ + if (walker((Node *) ((SubPlan *) expr->oper)->sublink, + context)) return true; } + /* for all Expr node types, examine args list */ + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; } break; case T_Aggref: @@ -918,11 +910,20 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) } break; case T_SubLink: - /* A "bare" SubLink (note we will not come here if we found - * a SUBPLAN_EXPR node above it). Examine the lefthand side, - * but not the oper list nor the subquery. - */ - return walker(((SubLink *) node)->lefthand, context); + { + SubLink *sublink = (SubLink *) node; + + /* If the SubLink has already been processed by subselect.c, + * it will have lefthand=NIL, and we only need to look at + * the oper list. Otherwise we only need to look at lefthand + * (the Oper nodes in the oper list are deemed uninteresting). + */ + if (sublink->lefthand) + return walker((Node *) sublink->lefthand, context); + else + return walker((Node *) sublink->oper, context); + } + break; case T_List: foreach(temp, (List *) node) { @@ -988,8 +989,8 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context) * the args and slink->oper lists (which belong to the outer plan), but it * will simply copy the link to the inner plan, since that's typically what * expression tree mutators want. A mutator that wants to modify the subplan - * can force appropriate behavior by recognizing subplan nodes and doing the - * right thing. + * can force appropriate behavior by recognizing subplan expression nodes + * and doing the right thing. * * Bare SubLink nodes (without a SUBPLAN_EXPR) are handled by recursing into * the "lefthand" argument list only. (A bare SubLink should be seen only if diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 0ecee29f52..a2280a7451 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -7,12 +7,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.56 1999/08/05 02:33:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.57 1999/08/25 23:21:34 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" + +#include "catalog/pg_operator.h" #include "nodes/makefuncs.h" #include "nodes/params.h" #include "nodes/relation.h" @@ -22,6 +24,7 @@ #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" +#include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "utils/builtins.h" @@ -209,7 +212,15 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) elog(ERROR, "parser: bad query in subselect"); sublink->subselect = (Node *) qtree; - if (sublink->subLinkType != EXISTS_SUBLINK) + if (sublink->subLinkType == EXISTS_SUBLINK) + { + /* EXISTS needs no lefthand or combining operator. + * These fields should be NIL already, but make sure. + */ + sublink->lefthand = NIL; + sublink->oper = NIL; + } + else { char *op = lfirst(sublink->oper); List *left_list = sublink->lefthand; @@ -236,27 +247,39 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) { TargetEntry *tent = (TargetEntry *) lfirst(right_list); Node *lexpr; - Expr *op_expr; + Operator optup; + Form_pg_operator opform; + Oper *newop; - if (! tent->resdom->resjunk) - { - if (left_list == NIL) - elog(ERROR, "parser: Subselect has too many fields."); - lexpr = lfirst(left_list); - left_list = lnext(left_list); - op_expr = make_op(op, lexpr, tent->expr); - if (op_expr->typeOid != BOOLOID && - sublink->subLinkType != EXPR_SUBLINK) - elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op); - sublink->oper = lappend(sublink->oper, op_expr); - } right_list = lnext(right_list); + if (tent->resdom->resjunk) + continue; + + if (left_list == NIL) + elog(ERROR, "parser: Subselect has too many fields."); + lexpr = lfirst(left_list); + left_list = lnext(left_list); + + optup = oper(op, + exprType(lexpr), + exprType(tent->expr), + FALSE); + opform = (Form_pg_operator) GETSTRUCT(optup); + + if (opform->oprresult != BOOLOID && + sublink->subLinkType != EXPR_SUBLINK) + elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op); + + newop = makeOper(oprid(optup),/* opno */ + InvalidOid, /* opid */ + opform->oprresult, + 0, + NULL); + sublink->oper = lappend(sublink->oper, newop); } if (left_list != NIL) elog(ERROR, "parser: Subselect has too few fields."); } - else - sublink->oper = NIL; result = (Node *) expr; break; } @@ -565,10 +588,13 @@ exprType(Node *expr) if (sublink->subLinkType == EXPR_SUBLINK) { - /* return the result type of the combining operator */ - Expr *op_expr = (Expr *) lfirst(sublink->oper); + /* return the result type of the combining operator; + * should only be one... + */ + Oper *op = (Oper *) lfirst(sublink->oper); - type = op_expr->typeOid; + Assert(IsA(op, Oper)); + type = op->opresulttype; } else { diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 88bef2237a..0fa7fd72d4 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -6,19 +6,23 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.54 1999/07/17 20:17:37 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.55 1999/08/25 23:21:43 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" +#include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/prep.h" #include "parser/analyze.h" +#include "parser/parse_expr.h" #include "parser/parse_relation.h" +#include "parser/parse_oper.h" #include "parser/parse_target.h" #include "parser/parse_type.h" #include "parser/parsetree.h" @@ -642,9 +646,6 @@ modifyAggrefUplevel(Node *node) modifyAggrefUplevel( (Node *) (sub->lefthand)); - modifyAggrefUplevel( - (Node *) (sub->oper)); - modifyAggrefUplevel( (Node *) (sub->subselect)); } @@ -816,12 +817,6 @@ modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int subl new_index, sublevels_up); - modifyAggrefChangeVarnodes( - (Node **) (&(sub->oper)), - rt_index, - new_index, - sublevels_up); - modifyAggrefChangeVarnodes( (Node **) (&(sub->subselect)), rt_index, @@ -989,6 +984,7 @@ modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr) SubLink *sub = (SubLink *) node; SubLink *osub = (SubLink *) orignode; + /* what about the lefthand? */ modifyAggrefDropQual( (Node **) (&(sub->subselect)), (Node *) (osub->subselect), @@ -1046,19 +1042,21 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) if (nodeTag(nth(1, exp->args)) == T_Aggref) elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported"); else - elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual"); + elog(ERROR, "rewrite: aggregate column of view must be at right side in qual"); } aggref = (Aggref *) nth(1, exp->args); target = (Var *) (aggref->target); + /* XXX bogus --- agg's target might not be a Var! */ rte = (RangeTblEntry *) nth(target->varno - 1, parsetree->rtable); + tle = makeNode(TargetEntry); resdom = makeNode(Resdom); - aggref->usenulls = TRUE; + aggref->usenulls = TRUE; /* XXX safe for all aggs?? */ resdom->resno = 1; - resdom->restype = ((Oper *) (exp->oper))->opresulttype; + resdom->restype = aggref->aggtype; resdom->restypmod = -1; resdom->resname = pstrdup(""); resdom->reskey = 0; @@ -1074,9 +1072,8 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) sublink = makeNode(SubLink); sublink->subLinkType = EXPR_SUBLINK; sublink->useor = FALSE; - sublink->lefthand = lappend(NIL, copyObject(lfirst(exp->args))); - sublink->oper = lappend(NIL, copyObject(exp)); - sublink->subselect = NULL; + sublink->lefthand = lcons(lfirst(exp->args), NIL); + sublink->oper = lcons(exp->oper, NIL); subquery = makeNode(Query); sublink->subselect = (Node *) subquery; @@ -1105,8 +1102,6 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) modifyAggrefChangeVarnodes((Node **) &(sublink->lefthand), target->varno, 1, target->varlevelsup); - modifyAggrefChangeVarnodes((Node **) &(sublink->oper), target->varno, - 1, target->varlevelsup); modifyAggrefChangeVarnodes((Node **) &(sublink->subselect), target->varno, 1, target->varlevelsup); @@ -1249,6 +1244,7 @@ modifyAggrefQual(Node **nodePtr, Query *parsetree) { SubLink *sub = (SubLink *) node; + /* lefthand ??? */ modifyAggrefQual( (Node **) (&(sub->subselect)), (Query *) (sub->subselect)); @@ -1318,9 +1314,6 @@ checkQueryHasSubLink_walker(Node *node, void *context) return false; if (IsA(node, SubLink)) return true; /* abort the tree traversal and return true */ - /* Note: we assume the tree has not yet been rewritten by subselect.c, - * therefore we will find bare SubLink nodes and not SUBPLAN nodes. - */ return expression_tree_walker(node, checkQueryHasSubLink_walker, context); } @@ -1654,8 +1647,6 @@ apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, in case T_SubLink: { SubLink *sub = (SubLink *) node; - List *tmp_lefthand, - *tmp_oper; apply_RIR_view( (Node **) (&(sub->lefthand)), @@ -1672,14 +1663,6 @@ apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, in tlist, modified, sublevels_up + 1); - - tmp_lefthand = sub->lefthand; - foreach(tmp_oper, sub->oper) - { - lfirst(((Expr *) lfirst(tmp_oper))->args) = - lfirst(tmp_lefthand); - tmp_lefthand = lnext(tmp_lefthand); - } } break; @@ -3014,31 +2997,47 @@ Except_Intersect_Rewrite(Query *parsetree) * of the targetlist must be (IN) or must not be (NOT IN) the * subselect */ + n->lefthand = NIL; foreach(elist, intersect_node->targetList) { - Node *expr = lfirst(elist); - TargetEntry *tent = (TargetEntry *) expr; + TargetEntry *tent = (TargetEntry *) lfirst(elist); n->lefthand = lappend(n->lefthand, tent->expr); } /* - * The first arguments of oper also have to be created for the - * sublink (they are the same as the lefthand side!) + * Also prepare the list of Opers that must be used for the + * comparisons (they depend on the specific datatypes involved!) */ left_expr = n->lefthand; right_expr = ((Query *) (n->subselect))->targetList; + n->oper = NIL; foreach(elist, left_expr) { Node *lexpr = lfirst(elist); - Node *rexpr = lfirst(right_expr); - TargetEntry *tent = (TargetEntry *) rexpr; - Expr *op_expr; + TargetEntry *tent = (TargetEntry *) lfirst(right_expr); + Operator optup; + Form_pg_operator opform; + Oper *newop; + + optup = oper(op, + exprType(lexpr), + exprType(tent->expr), + FALSE); + opform = (Form_pg_operator) GETSTRUCT(optup); + + if (opform->oprresult != BOOLOID) + elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op); + + newop = makeOper(oprid(optup),/* opno */ + InvalidOid, /* opid */ + opform->oprresult, + 0, + NULL); - op_expr = make_op(op, lexpr, tent->expr); + n->oper = lappend(n->oper, newop); - n->oper = lappend(n->oper, op_expr); right_expr = lnext(right_expr); } diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index f88e936412..16b31eae84 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.39 1999/08/21 03:49:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.40 1999/08/25 23:21:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -137,13 +137,7 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) case T_SubLink: { SubLink *sub = (SubLink *) node; - List *tmp_oper, - *tmp_lefthand; - /* - * We also have to adapt the variables used in - * sub->lefthand and sub->oper - */ OffsetVarNodes( (Node *) (sub->lefthand), offset, @@ -153,20 +147,6 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) (Node *) (sub->subselect), offset, sublevels_up + 1); - - /* - * Make sure the first argument of sub->oper points to the - * same var as sub->lefthand does otherwise we will run - * into troubles using aggregates (aggno will not be set - * correctly) - */ - tmp_lefthand = sub->lefthand; - foreach(tmp_oper, sub->oper) - { - lfirst(((Expr *) lfirst(tmp_oper))->args) = - lfirst(tmp_lefthand); - tmp_lefthand = lnext(tmp_lefthand); - } } break; @@ -357,8 +337,6 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) case T_SubLink: { SubLink *sub = (SubLink *) node; - List *tmp_oper, - *tmp_lefthand; ChangeVarNodes( (Node *) (sub->lefthand), @@ -371,20 +349,6 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) rt_index, new_index, sublevels_up + 1); - - /* - * Make sure the first argument of sub->oper points to the - * same var as sub->lefthand does otherwise we will run - * into troubles using aggregates (aggno will not be set - * correctly) - */ - tmp_lefthand = sub->lefthand; - foreach(tmp_oper, sub->oper) - { - lfirst(((Expr *) lfirst(tmp_oper))->args) = - lfirst(tmp_lefthand); - tmp_lefthand = lnext(tmp_lefthand); - } } break; @@ -732,6 +696,7 @@ ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr, SubLink *sublink = (SubLink *) node; Query *query = (Query *) sublink->subselect; + /* XXX what about lefthand? What about rest of subquery? */ ResolveNew(info, targetlist, (Node **) &(query->qual), sublevels_up + 1); } break; @@ -888,6 +853,7 @@ nodeHandleRIRAttributeRule(Node **nodePtr, SubLink *sublink = (SubLink *) node; Query *query = (Query *) sublink->subselect; + /* XXX what about lefthand? What about rest of subquery? */ nodeHandleRIRAttributeRule((Node **) &(query->qual), rtable, targetlist, rt_index, attr_num, modified, badsql, sublevels_up + 1); @@ -1062,9 +1028,6 @@ nodeHandleViewRule(Node **nodePtr, { SubLink *sublink = (SubLink *) node; Query *query = (Query *) sublink->subselect; - List *tmp_lefthand, - *tmp_oper; - nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist, rt_index, modified, sublevels_up + 1); @@ -1078,30 +1041,10 @@ nodeHandleViewRule(Node **nodePtr, /* * We also have to adapt the variables used in - * sublink->lefthand and sublink->oper + * sublink->lefthand */ nodeHandleViewRule((Node **) &(sublink->lefthand), rtable, targetlist, rt_index, modified, sublevels_up); - - /* - * Make sure the first argument of sublink->oper points to - * the same var as sublink->lefthand does otherwise we - * will run into troubles using aggregates (aggno will not - * be set correctly - */ - pfree(lfirst(((Expr *) lfirst(sublink->oper))->args)); - lfirst(((Expr *) lfirst(sublink->oper))->args) = - lfirst(sublink->lefthand); - - - /* INTERSECT want's this - Jan */ - - /* - * tmp_lefthand = sublink->lefthand; foreach(tmp_oper, - * sublink->oper) { lfirst(((Expr *) - * lfirst(tmp_oper))->args) = lfirst(tmp_lefthand); - * tmp_lefthand = lnext(tmp_lefthand); } - */ } break; default: diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 008402e26f..ea7950ec50 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.22 1999/08/21 03:48:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.23 1999/08/25 23:21:35 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1586,7 +1586,7 @@ get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) { SubLink *sublink = (SubLink *) node; Query *query = (Query *) (sublink->subselect); - Expr *expr; + Oper *oper; List *l; char *sep; char buf[BUFSIZE]; @@ -1620,20 +1620,20 @@ get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix) break; case ANY_SUBLINK: - expr = (Expr *) lfirst(sublink->oper); - strcat(buf, get_opname(((Oper *) (expr->oper))->opno)); + oper = (Oper *) lfirst(sublink->oper); + strcat(buf, get_opname(oper->opno)); strcat(buf, " ANY "); break; case ALL_SUBLINK: - expr = (Expr *) lfirst(sublink->oper); - strcat(buf, get_opname(((Oper *) (expr->oper))->opno)); + oper = (Oper *) lfirst(sublink->oper); + strcat(buf, get_opname(oper->opno)); strcat(buf, " ALL "); break; case EXPR_SUBLINK: - expr = (Expr *) lfirst(sublink->oper); - strcat(buf, get_opname(((Oper *) (expr->oper))->opno)); + oper = (Oper *) lfirst(sublink->oper); + strcat(buf, get_opname(oper->opno)); strcat(buf, " "); break; @@ -1766,6 +1766,7 @@ check_if_rte_used(int rt_index, Node *node, int sup) if (check_if_rte_used(rt_index, (Node *) (query->qual), sup + 1)) return TRUE; + /* why aren't we looking at query->targetlist, havingQual? */ if (check_if_rte_used(rt_index, (Node *) (sublink->lefthand), sup)) return TRUE; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 10e51e4026..e2ef42218f 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.35 1999/08/22 20:15:00 tgl Exp $ + * $Id: primnodes.h,v 1.36 1999/08/25 23:21:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -319,10 +319,36 @@ typedef struct Aggref /* ---------------- * SubLink * subLinkType - EXISTS, ALL, ANY, EXPR - * useor - TRUE for <> - * lefthand - list of Var/Const nodes on the left + * useor - TRUE for <> (combine op results with "or" not "and") + * lefthand - list of outer-query expressions on the left * oper - list of Oper nodes * subselect - subselect as Query* or parsetree + * + * 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 + * lefthand, and sets oper to a one-element list containing the string + * name of the operator. + * 2. The parser's expression transformation transforms lefthand normally, + * and replaces oper with a list of Oper nodes, one per lefthand + * expression. These nodes represent the parser's resolution of exactly + * which operator to apply to each pair of lefthand and targetlist + * expressions. However, we have not constructed actual Expr trees for + * these operators yet. This is the representation seen in saved rules + * and in the rewriter. + * 3. Finally, the planner converts the oper list to a list of normal Expr + * nodes representing the application of the operator(s) to the lefthand + * expressions and values from the inner targetlist. The inner + * targetlist items are represented by placeholder Param or Const nodes. + * The lefthand field is set to NIL, since its expressions are now in + * the Expr list. This representation is passed to the executor. + * + * Planner routines that might see either representation 2 or 3 can tell + * the difference by checking whether lefthand is NIL or not. Also, + * 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. * ---------------- */ typedef enum SubLinkType -- 2.40.0