Revise implementation of SubLinks so that there is a consistent,
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 25 Aug 1999 23:21:43 +0000 (23:21 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 25 Aug 1999 23:21:43 +0000 (23:21 +0000)
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
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_expr.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/primnodes.h

index e4e83d654ac52252fd070c587cbcb34a660155f1..4bd0eb2ff31c39bdb222b0bf87d4c8799204cf07 100644 (file)
@@ -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)
index c275b7adc457a819af76ee383bbeeda6e00f3ac1..7e5d2be749eabecce6b9e8b84f52891cead33957 100644 (file)
@@ -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);
index fbb5a98e83db1501a7f779c863010f061a60c3ea..2d960b5cf03ac21cb9b284b062101f262ae0120d 100644 (file)
@@ -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
index 0ecee29f52d8b7ed989a5a1546f21692cd28d5af..a2280a745112cd06b693dab1e6288da8ca60952d 100644 (file)
@@ -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
                                {
index 88bef2237aa65724fa88d91c11aedb10626e9982..0fa7fd72d43681bfef5a290d7f857c40f8577ab8 100644 (file)
@@ -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("<noname>");
        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);
                        }
 
index f88e936412ac352e7fb412c2b9ac85691aee18d4..16b31eae84d0f97c186687bd640989cc453c49f3 100644 (file)
@@ -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:
index 008402e26f311473aaa545e10309258872e15761..ea7950ec5020ff072491c706c22719c49697bf17 100644 (file)
@@ -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;
index 10e51e40268eed36463e7e0edd453d888c7c19eb..e2ef42218fc5b967c6a064f4d9813c66541fcd70 100644 (file)
@@ -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