OSDN Git Service

Be more realistic about plans involving Materialize nodes: take their
[pg-rex/syncrep.git] / src / backend / optimizer / plan / subselect.c
index 03e3837..e4bbd29 100644 (file)
@@ -3,11 +3,11 @@
  * subselect.c
  *       Planning routines for subselects and parameters.
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.43 2000/10/05 19:11:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.58 2002/11/30 05:21:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/subselect.h"
+#include "parser/parsetree.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_oper.h"
-#include "utils/lsyscache.h"
+#include "utils/syscache.h"
 
 
 Index          PlannerQueryLevel;      /* level of current query */
@@ -43,24 +44,32 @@ int                 PlannerPlanId = 0;      /* to assign unique ID to subquery plans */
  * and update the list elements when we enter or exit a subplan
  * recursion level.  But we must pay attention not to confuse this
  * meaning with the normal meaning of varlevelsup.
+ *
+ * We also need to create Param slots that don't correspond to any outer Var.
+ * For these, we set varno = 0 and varlevelsup = 0, so that they can't
+ * accidentally match an outer Var.
  *--------------------
  */
 
 
+static void convert_sublink_opers(SubLink *slink, List *targetlist,
+                                                                 List **setParams);
+
+
 /*
  * Create a new entry in the PlannerParamVar list, and return its index.
  *
- * var contains the data to be copied, except for varlevelsup which
- * is set from the absolute level value given by varlevel.
+ * var contains the data to use, except for varlevelsup which
+ * is set from the absolute level value given by varlevel.  NOTE that
+ * the passed var is scribbled on and placed directly into the list!
+ * Generally, caller should have just created or copied it.
  */
 static int
 new_param(Var *var, Index varlevel)
 {
-       Var                *paramVar = (Var *) copyObject(var);
+       var->varlevelsup = varlevel;
 
-       paramVar->varlevelsup = varlevel;
-
-       PlannerParamVar = lappend(PlannerParamVar, paramVar);
+       PlannerParamVar = lappend(PlannerParamVar, var);
 
        return length(PlannerParamVar) - 1;
 }
@@ -82,12 +91,12 @@ replace_var(Var *var)
 
        /*
         * If there's already a PlannerParamVar entry for this same Var, just
-        * use it.      NOTE: in sufficiently complex querytrees, it is
-        * possible for the same varno/varlevel to refer to different RTEs in
-        * different parts of the parsetree, so that different fields might
-        * end up sharing the same Param number.  As long as we check the
-        * vartype as well, I believe that this sort of aliasing will cause no
-        * trouble. The correct field should get stored into the Param slot at
+        * use it.      NOTE: in sufficiently complex querytrees, it is possible
+        * for the same varno/varlevel to refer to different RTEs in different
+        * parts of the parsetree, so that different fields might end up
+        * sharing the same Param number.  As long as we check the vartype as
+        * well, I believe that this sort of aliasing will cause no trouble.
+        * The correct field should get stored into the Param slot at
         * execution in each part of the tree.
         */
        i = 0;
@@ -106,7 +115,7 @@ replace_var(Var *var)
        if (!ppv)
        {
                /* Nope, so make a new one */
-               i = new_param(var, varlevel);
+               i = new_param((Var *) copyObject(var), varlevel);
        }
 
        retval = makeNode(Param);
@@ -118,6 +127,22 @@ replace_var(Var *var)
 }
 
 /*
+ * Generate a new Param node that will not conflict with any other.
+ */
+static Param *
+generate_new_param(Oid paramtype, int32 paramtypmod)
+{
+       Var                *var = makeVar(0, 0, paramtype, paramtypmod, 0);
+       Param      *retval = makeNode(Param);
+
+       retval->paramkind = PARAM_EXEC;
+       retval->paramid = (AttrNumber) new_param(var, 0);
+       retval->paramtype = paramtype;
+
+       return retval;
+}
+
+/*
  * Convert a bare SubLink (as created by the parser) into a SubPlan.
  */
 static Node *
@@ -125,6 +150,7 @@ make_subplan(SubLink *slink)
 {
        SubPlan    *node = makeNode(SubPlan);
        Query      *subquery = (Query *) (slink->subselect);
+       Oid                     result_type = exprType((Node *) slink);
        double          tuple_fraction;
        Plan       *plan;
        List       *lst;
@@ -141,10 +167,10 @@ make_subplan(SubLink *slink)
                elog(ERROR, "make_subplan: invalid expression structure (subquery already processed?)");
 
        /*
-        * Copy the source Query node.  This is a quick and dirty kluge to resolve
-        * the fact that the parser can generate trees with multiple links to the
-        * same sub-Query node, but the planner wants to scribble on the Query.
-        * Try to clean this up when we do querytree redesign...
+        * Copy the source Query node.  This is a quick and dirty kluge to
+        * resolve the fact that the parser can generate trees with multiple
+        * links to the same sub-Query node, but the planner wants to scribble
+        * on the Query. Try to clean this up when we do querytree redesign...
         */
        subquery = (Query *) copyObject(subquery);
 
@@ -182,7 +208,8 @@ make_subplan(SubLink *slink)
         */
        node->plan = plan = subquery_planner(subquery, tuple_fraction);
 
-       node->plan_id = PlannerPlanId++; /* Assign unique ID to this SubPlan */
+       node->plan_id = PlannerPlanId++;        /* Assign unique ID to this
+                                                                                * SubPlan */
 
        node->rtable = subquery->rtable;
        node->sublink = slink;
@@ -190,8 +217,8 @@ make_subplan(SubLink *slink)
        slink->subselect = NULL;        /* cool ?! see error check above! */
 
        /*
-        * Make parParam list of params that current query level will pass
-        * to this child plan.
+        * Make parParam list of params that current query level will pass to
+        * this child plan.
         */
        foreach(lst, plan->extParam)
        {
@@ -213,13 +240,9 @@ make_subplan(SubLink *slink)
         */
        if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
        {
-               Var                *var = makeVar(0, 0, BOOLOID, -1, 0);
-               Param      *prm = makeNode(Param);
+               Param      *prm;
 
-               prm->paramkind = PARAM_EXEC;
-               prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
-               prm->paramtype = var->vartype;
-               pfree(var);                             /* var is only needed for new_param */
+               prm = generate_new_param(BOOLOID, -1);
                node->setParam = lappendi(node->setParam, prm->paramid);
                PlannerInitPlan = lappend(PlannerInitPlan, node);
                result = (Node *) prm;
@@ -227,84 +250,27 @@ make_subplan(SubLink *slink)
        else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
        {
                TargetEntry *te = lfirst(plan->targetlist);
+               Param      *prm;
 
-               /* need a var node just to pass to new_param()... */
-               Var                *var = makeVar(0, 0, te->resdom->restype,
-                                                                 te->resdom->restypmod, 0);
-               Param      *prm = makeNode(Param);
-
-               prm->paramkind = PARAM_EXEC;
-               prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
-               prm->paramtype = var->vartype;
-               pfree(var);                             /* var is only needed for new_param */
+               prm = generate_new_param(te->resdom->restype, te->resdom->restypmod);
                node->setParam = lappendi(node->setParam, prm->paramid);
                PlannerInitPlan = lappend(PlannerInitPlan, node);
                result = (Node *) prm;
        }
        else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
        {
-               List       *newoper = NIL;
-               int                     i = 0;
-
-               /*
-                * Convert oper list of Opers into a list of Exprs, using lefthand
-                * arguments and Params representing inside results.
-                */
-               foreach(lst, slink->oper)
-               {
-                       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->paramtype = var->vartype;
-                       pfree(var);                     /* var is only needed for new_param */
-
-                       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);
-                       i++;
-               }
-               slink->oper = newoper;
-               slink->lefthand = NIL;
+               convert_sublink_opers(slink, plan->targetlist, &node->setParam);
                PlannerInitPlan = lappend(PlannerInitPlan, node);
-               if (i > 1)
-                       result = (Node *) ((slink->useor) ? make_orclause(newoper) :
-                                                          make_andclause(newoper));
+               if (length(slink->oper) > 1)
+                       result = (Node *) ((slink->useor) ? make_orclause(slink->oper) :
+                                                          make_andclause(slink->oper));
                else
-                       result = (Node *) lfirst(newoper);
+                       result = (Node *) lfirst(slink->oper);
        }
        else
        {
                Expr       *expr = makeNode(Expr);
                List       *args = NIL;
-               List       *newoper = NIL;
-               int                     i = 0;
 
                /*
                 * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types
@@ -317,6 +283,12 @@ make_subplan(SubLink *slink)
                 * is anything more complicated than a plain sequential scan, and
                 * we do it even for seqscan if the qual appears selective enough
                 * to eliminate many tuples.
+                *
+                * XXX It's pretty ugly to be inserting a MATERIAL node at this
+                * point.  Since subquery_planner has already run SS_finalize_plan
+                * on the subplan tree, we have to kluge up parameter lists for
+                * the MATERIAL node.  Possibly this could be fixed by postponing
+                * SS_finalize_plan processing until setrefs.c is run.
                 */
                if (node->parParam == NIL)
                {
@@ -339,13 +311,13 @@ make_subplan(SubLink *slink)
                                        }
                                        break;
                                case T_Material:
+                               case T_FunctionScan:
                                case T_Sort:
 
                                        /*
                                         * Don't add another Material node if there's one
-                                        * already, nor if the top node is a Sort, since Sort
-                                        * materializes its output anyway.      (I doubt either
-                                        * case can happen in practice for a subplan, but...)
+                                        * already, nor if the top node is any other type that
+                                        * materializes its output anyway.
                                         */
                                        use_material = false;
                                        break;
@@ -355,15 +327,31 @@ make_subplan(SubLink *slink)
                        }
                        if (use_material)
                        {
-                               plan = (Plan *) make_material(plan->targetlist, plan);
-                               node->plan = plan;
+                               Plan       *matplan;
+                               Path            matpath; /* dummy for result of cost_material */
+
+                               matplan = (Plan *) make_material(plan->targetlist, plan);
+                               /* need to calculate costs */
+                               cost_material(&matpath,
+                                                         plan->total_cost,
+                                                         plan->plan_rows,
+                                                         plan->plan_width);
+                               matplan->startup_cost = matpath.startup_cost;
+                               matplan->total_cost = matpath.total_cost;
+                               /* parameter kluge --- see comments above */
+                               matplan->extParam = listCopy(plan->extParam);
+                               matplan->locParam = listCopy(plan->locParam);
+                               node->plan = plan = matplan;
                        }
                }
 
+               /* Fix the SubLink's oper list */
+               convert_sublink_opers(slink, plan->targetlist, NULL);
+
                /*
                 * Make expression of SUBPLAN type
                 */
-               expr->typeOid = BOOLOID;/* bogus, but we don't really care */
+               expr->typeOid = result_type;
                expr->opType = SUBPLAN_EXPR;
                expr->oper = (Node *) node;
 
@@ -386,52 +374,82 @@ make_subplan(SubLink *slink)
                }
                expr->args = args;
 
+               result = (Node *) expr;
+       }
+
+       return result;
+}
+
+/*
+ * convert_sublink_opers: convert a SubLink's oper list from the
+ * parser/rewriter format into the executor's format.
+ *
+ * The oper list is initially just a list of Oper nodes.  We replace it
+ * with a list of actually executable expressions, in which the specified
+ * operators are applied to corresponding elements of the lefthand list
+ * and Params representing the results of the subplan.  lefthand is then
+ * set to NIL.
+ *
+ * If setParams is not NULL, the paramids of the Params created are added
+ * to the *setParams list.
+ */
+static void
+convert_sublink_opers(SubLink *slink, List *targetlist,
+                                         List **setParams)
+{
+       List       *newoper = NIL;
+       List       *leftlist = slink->lefthand;
+       List       *lst;
+
+       foreach(lst, slink->oper)
+       {
+               Oper       *oper = (Oper *) lfirst(lst);
+               Node       *lefthand = lfirst(leftlist);
+               TargetEntry *te = lfirst(targetlist);
+               Param      *prm;
+               Operator        tup;
+               Form_pg_operator opform;
+               Node       *left,
+                                  *right;
+
+               /* Make the Param node representing the subplan's result */
+               prm = generate_new_param(te->resdom->restype,
+                                                                te->resdom->restypmod);
+
+               /* Record its ID if needed */
+               if (setParams)
+                       *setParams = lappendi(*setParams, prm->paramid);
+
+               /* Look up the operator to check its declared input types */
+               Assert(IsA(oper, Oper));
+               tup = SearchSysCache(OPEROID,
+                                                        ObjectIdGetDatum(oper->opno),
+                                                        0, 0, 0);
+               if (!HeapTupleIsValid(tup))
+                       elog(ERROR, "cache lookup failed for operator %u", oper->opno);
+               opform = (Form_pg_operator) GETSTRUCT(tup);
+
                /*
-                * Convert oper list of Opers into a list of Exprs, using lefthand
-                * arguments and Consts representing inside results.
+                * Make the expression node.
+                *
+                * Note: we use make_operand in case runtime type conversion
+                * function calls must be inserted for this operator!
                 */
-               foreach(lst, slink->oper)
-               {
-                       Oper       *oper = (Oper *) lfirst(lst);
-                       Node       *lefthand = nth(i, slink->lefthand);
-                       TargetEntry *te = nth(i, plan->targetlist);
-                       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);
+               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));
 
-                       Assert(IsA(oper, Oper));
-                       tup = get_operator_tuple(oper->opno);
-                       Assert(HeapTupleIsValid(tup));
-                       opform = (Form_pg_operator) GETSTRUCT(tup);
+               ReleaseSysCache(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++;
-               }
-               slink->oper = newoper;
-               slink->lefthand = NIL;
-               result = (Node *) expr;
+               leftlist = lnext(leftlist);
+               targetlist = lnext(targetlist);
        }
 
-       return result;
+       slink->oper = newoper;
+       slink->lefthand = NIL;
 }
 
 /*
@@ -567,7 +585,7 @@ process_sublinks_mutator(Node *node, void *context)
 }
 
 List *
-SS_finalize_plan(Plan *plan)
+SS_finalize_plan(Plan *plan, List *rtable)
 {
        List       *extParam = NIL;
        List       *locParam = NIL;
@@ -600,17 +618,6 @@ SS_finalize_plan(Plan *plan)
                                                          &results);
                        break;
 
-               case T_Append:
-                       foreach(lst, ((Append *) plan)->appendplans)
-                               results.paramids = set_unioni(results.paramids,
-                                                                SS_finalize_plan((Plan *) lfirst(lst)));
-                       break;
-
-               case T_SubqueryScan:
-                       results.paramids = set_unioni(results.paramids,
-                                               SS_finalize_plan(((SubqueryScan *) plan)->subplan));
-                       break;
-
                case T_IndexScan:
                        finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
                                                          &results);
@@ -622,6 +629,42 @@ SS_finalize_plan(Plan *plan)
                         */
                        break;
 
+               case T_TidScan:
+                       finalize_primnode((Node *) ((TidScan *) plan)->tideval,
+                                                         &results);
+                       break;
+
+               case T_SubqueryScan:
+
+                       /*
+                        * In a SubqueryScan, SS_finalize_plan has already been run on
+                        * the subplan by the inner invocation of subquery_planner, so
+                        * there's no need to do it again.  Instead, just pull out the
+                        * subplan's extParams list, which represents the params it
+                        * needs from my level and higher levels.
+                        */
+                       results.paramids = set_unioni(results.paramids,
+                                                        ((SubqueryScan *) plan)->subplan->extParam);
+                       break;
+
+               case T_FunctionScan:
+                       {
+                               RangeTblEntry *rte;
+
+                               rte = rt_fetch(((FunctionScan *) plan)->scan.scanrelid,
+                                                          rtable);
+                               Assert(rte->rtekind == RTE_FUNCTION);
+                               finalize_primnode(rte->funcexpr, &results);
+                       }
+                       break;
+
+               case T_Append:
+                       foreach(lst, ((Append *) plan)->appendplans)
+                               results.paramids = set_unioni(results.paramids,
+                                                                  SS_finalize_plan((Plan *) lfirst(lst),
+                                                                                                       rtable));
+                       break;
+
                case T_NestLoop:
                        finalize_primnode((Node *) ((Join *) plan)->joinqual,
                                                          &results);
@@ -642,12 +685,7 @@ SS_finalize_plan(Plan *plan)
                        break;
 
                case T_Hash:
-                       finalize_primnode(((Hash *) plan)->hashkey,
-                                                         &results);
-                       break;
-
-               case T_TidScan:
-                       finalize_primnode((Node *) ((TidScan *) plan)->tideval,
+                       finalize_primnode((Node *) ((Hash *) plan)->hashkeys,
                                                          &results);
                        break;
 
@@ -657,6 +695,7 @@ SS_finalize_plan(Plan *plan)
                case T_Sort:
                case T_Unique:
                case T_SetOp:
+               case T_Limit:
                case T_Group:
                        break;
 
@@ -667,9 +706,11 @@ SS_finalize_plan(Plan *plan)
 
        /* Process left and right subplans, if any */
        results.paramids = set_unioni(results.paramids,
-                                                                 SS_finalize_plan(plan->lefttree));
+                                                                 SS_finalize_plan(plan->lefttree,
+                                                                                                  rtable));
        results.paramids = set_unioni(results.paramids,
-                                                                 SS_finalize_plan(plan->righttree));
+                                                                 SS_finalize_plan(plan->righttree,
+                                                                                                  rtable));
 
        /* Now we have all the paramids and subplans */