]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/plan/planner.c
Change the planner-to-executor API so that the planner tells the executor
[postgresql] / src / backend / optimizer / plan / planner.c
index c02f6e195cd57deb7827ae1fc25560fb599a3147..5fcfc58bf9b3acc642c3a4600dec96a06b55ab2e 100644 (file)
@@ -3,12 +3,12 @@
  * planner.c
  *       The query optimizer external interface.
  *
- * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.205 2006/07/26 19:31:50 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.211 2007/01/10 18:06:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_oper.h"
 #include "parser/parsetree.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -48,22 +49,25 @@ ParamListInfo PlannerBoundParamList = NULL;         /* current boundParams */
 #define EXPRKIND_QUAL          0
 #define EXPRKIND_TARGET                1
 #define EXPRKIND_RTFUNC                2
-#define EXPRKIND_LIMIT         3
-#define EXPRKIND_ININFO                4
-#define EXPRKIND_APPINFO       5
+#define EXPRKIND_VALUES                3
+#define EXPRKIND_LIMIT         4
+#define EXPRKIND_ININFO                5
+#define EXPRKIND_APPINFO       6
 
 
 static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
 static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
 static Plan *inheritance_planner(PlannerInfo *root);
 static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);
+static bool is_dummy_plan(Plan *plan);
 static double preprocess_limit(PlannerInfo *root,
                                 double tuple_fraction,
                                 int64 *offset_est, int64 *count_est);
+static Oid *extract_grouping_ops(List *groupClause);
 static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
                                           Path *cheapest_path, Path *sorted_path,
-                                          double dNumGroups, AggClauseCounts *agg_counts);
-static bool hash_safe_grouping(PlannerInfo *root);
+                                          Oid *groupOperators, double dNumGroups,
+                                          AggClauseCounts *agg_counts);
 static List *make_subplanTargetList(PlannerInfo *root, List *tlist,
                                           AttrNumber **groupColIdx, bool *need_tlist_eval);
 static void locate_grouping_columns(PlannerInfo *root,
@@ -278,6 +282,10 @@ subquery_planner(Query *parse, double tuple_fraction,
                preprocess_expression(root, (Node *) parse->targetList,
                                                          EXPRKIND_TARGET);
 
+       parse->returningList = (List *)
+               preprocess_expression(root, (Node *) parse->returningList,
+                                                         EXPRKIND_TARGET);
+
        preprocess_qual_conditions(root, (Node *) parse->jointree);
 
        parse->havingQual = preprocess_expression(root, parse->havingQual,
@@ -295,7 +303,7 @@ subquery_planner(Query *parse, double tuple_fraction,
                preprocess_expression(root, (Node *) root->append_rel_list,
                                                          EXPRKIND_APPINFO);
 
-       /* Also need to preprocess expressions for function RTEs */
+       /* Also need to preprocess expressions for function and values RTEs */
        foreach(l, parse->rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
@@ -303,6 +311,10 @@ subquery_planner(Query *parse, double tuple_fraction,
                if (rte->rtekind == RTE_FUNCTION)
                        rte->funcexpr = preprocess_expression(root, rte->funcexpr,
                                                                                                  EXPRKIND_RTFUNC);
+               else if (rte->rtekind == RTE_VALUES)
+                       rte->values_lists = (List *)
+                               preprocess_expression(root, (Node *) rte->values_lists,
+                                                                         EXPRKIND_VALUES);
        }
 
        /*
@@ -417,9 +429,11 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
        /*
         * If the query has any join RTEs, replace join alias variables with
         * base-relation variables. We must do this before sublink processing,
-        * else sublinks expanded out from join aliases wouldn't get processed.
+        * else sublinks expanded out from join aliases wouldn't get processed. We
+        * can skip it in VALUES lists, however, since they can't contain any Vars
+        * at all.
         */
-       if (root->hasJoinRTEs)
+       if (root->hasJoinRTEs && kind != EXPRKIND_VALUES)
                expr = flatten_join_alias_vars(root, expr);
 
        /*
@@ -437,10 +451,14 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
         * and we will waste cycles copying the tree.  Notice however that we
         * still must do it for quals (to get AND/OR flatness); and if we are in a
         * subquery we should not assume it will be done only once.
+        *
+        * For VALUES lists we never do this at all, again on the grounds that we
+        * should optimize for one-time evaluation.
         */
-       if (root->parse->jointree->fromlist != NIL ||
-               kind == EXPRKIND_QUAL ||
-               PlannerQueryLevel > 1)
+       if (kind != EXPRKIND_VALUES &&
+               (root->parse->jointree->fromlist != NIL ||
+                kind == EXPRKIND_QUAL ||
+                PlannerQueryLevel > 1))
                expr = eval_const_expressions(expr);
 
        /*
@@ -465,7 +483,7 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
         * SS_replace_correlation_vars ...
         */
 
-       /* Replace uplevel vars with Param nodes */
+       /* Replace uplevel vars with Param nodes (this IS possible in VALUES) */
        if (PlannerQueryLevel > 1)
                expr = SS_replace_correlation_vars(expr);
 
@@ -542,14 +560,13 @@ inheritance_planner(PlannerInfo *root)
        Query      *parse = root->parse;
        int                     parentRTindex = parse->resultRelation;
        List       *subplans = NIL;
+       List       *resultRelations = NIL;
+       List       *returningLists = NIL;
+       List       *rtable = NIL;
        List       *tlist = NIL;
        PlannerInfo subroot;
        ListCell   *l;
 
-       subroot.parse = NULL;           /* catch it if no matches in loop */
-
-       parse->resultRelations = NIL;
-
        foreach(l, root->append_rel_list)
        {
                AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
@@ -559,10 +576,6 @@ inheritance_planner(PlannerInfo *root)
                if (appinfo->parent_relid != parentRTindex)
                        continue;
 
-               /* Build target-relations list for the executor */
-               parse->resultRelations = lappend_int(parse->resultRelations,
-                                                                                        appinfo->child_relid);
-
                /*
                 * Generate modified query with this rel as target.  We have to be
                 * prepared to translate varnos in in_info_list as well as in the
@@ -581,13 +594,49 @@ inheritance_planner(PlannerInfo *root)
                /* Generate plan */
                subplan = grouping_planner(&subroot, 0.0 /* retrieve all tuples */ );
 
-               subplans = lappend(subplans, subplan);
+               /*
+                * If this child rel was excluded by constraint exclusion, exclude it
+                * from the plan.
+                */
+               if (is_dummy_plan(subplan))
+                       continue;
 
-               /* Save preprocessed tlist from first rel for use in Append */
-               if (tlist == NIL)
+               /* Save rtable and tlist from first rel for use below */
+               if (subplans == NIL)
+               {
+                       rtable = subroot.parse->rtable;
                        tlist = subplan->targetlist;
+               }
+
+               subplans = lappend(subplans, subplan);
+
+               /* Build target-relations list for the executor */
+               resultRelations = lappend_int(resultRelations, appinfo->child_relid);
+
+               /* Build list of per-relation RETURNING targetlists */
+               if (parse->returningList)
+               {
+                       Assert(list_length(subroot.parse->returningLists) == 1);
+                       returningLists = list_concat(returningLists,
+                                                                                subroot.parse->returningLists);
+               }
        }
 
+       parse->resultRelations = resultRelations;
+       parse->returningLists = returningLists;
+
+       /* Mark result as unordered (probably unnecessary) */
+       root->query_pathkeys = NIL;
+
+       /*
+        * If we managed to exclude every child rel, return a dummy plan
+        */
+       if (subplans == NIL)
+               return (Plan *) make_result(tlist,
+                                                                       (Node *) list_make1(makeBoolConst(false,
+                                                                                                                                         false)),
+                                                                       NULL);
+
        /*
         * Planning might have modified the rangetable, due to changes of the
         * Query structures inside subquery RTEs.  We have to ensure that this
@@ -599,10 +648,7 @@ inheritance_planner(PlannerInfo *root)
         *
         * XXX should clean this up someday
         */
-       parse->rtable = subroot.parse->rtable;
-
-       /* Mark result as unordered (probably unnecessary) */
-       root->query_pathkeys = NIL;
+       parse->rtable = rtable;
 
        return (Plan *) make_append(subplans, true, tlist);
 }
@@ -706,6 +752,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                List       *sub_tlist;
                List       *group_pathkeys;
                AttrNumber *groupColIdx = NULL;
+               Oid                *groupOperators = NULL;
                bool            need_tlist_eval = true;
                QualCost        tlist_cost;
                Path       *cheapest_path;
@@ -785,14 +832,17 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                sort_pathkeys = root->sort_pathkeys;
 
                /*
-                * If grouping, decide whether we want to use hashed grouping.
+                * If grouping, extract the grouping operators and decide whether we
+                * want to use hashed grouping.
                 */
                if (parse->groupClause)
                {
+                       groupOperators = extract_grouping_ops(parse->groupClause);
                        use_hashed_grouping =
                                choose_hashed_grouping(root, tuple_fraction,
                                                                           cheapest_path, sorted_path,
-                                                                          dNumGroups, &agg_counts);
+                                                                          groupOperators, dNumGroups,
+                                                                          &agg_counts);
 
                        /* Also convert # groups to long int --- but 'ware overflow! */
                        numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
@@ -912,6 +962,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                                                                                                AGG_HASHED,
                                                                                                numGroupCols,
                                                                                                groupColIdx,
+                                                                                               groupOperators,
                                                                                                numGroups,
                                                                                                agg_counts.numAggs,
                                                                                                result_plan);
@@ -955,6 +1006,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                                                                                                aggstrategy,
                                                                                                numGroupCols,
                                                                                                groupColIdx,
+                                                                                               groupOperators,
                                                                                                numGroups,
                                                                                                agg_counts.numAggs,
                                                                                                result_plan);
@@ -983,6 +1035,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                                                                                                  (List *) parse->havingQual,
                                                                                                  numGroupCols,
                                                                                                  groupColIdx,
+                                                                                                 groupOperators,
                                                                                                  dNumGroups,
                                                                                                  result_plan);
                                /* The Group node won't change sort ordering */
@@ -1052,6 +1105,21 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
                                                                                  count_est);
        }
 
+       /*
+        * Deal with the RETURNING clause if any.  It's convenient to pass the
+        * returningList through setrefs.c now rather than at top level (if we
+        * waited, handling inherited UPDATE/DELETE would be much harder).
+        */
+       if (parse->returningList)
+       {
+               List       *rlist;
+
+               rlist = set_returning_clause_references(parse->returningList,
+                                                                                               result_plan,
+                                                                                               parse->resultRelation);
+               parse->returningLists = list_make1(rlist);
+       }
+
        /*
         * Return the actual output ordering in query_pathkeys for possible use by
         * an outer query level.
@@ -1061,6 +1129,35 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
        return result_plan;
 }
 
+/*
+ * Detect whether a plan node is a "dummy" plan created when a relation
+ * is deemed not to need scanning due to constraint exclusion.
+ *
+ * Currently, such dummy plans are Result nodes with constant FALSE
+ * filter quals.
+ */
+static bool
+is_dummy_plan(Plan *plan)
+{
+       if (IsA(plan, Result))
+       {
+               List       *rcqual = (List *) ((Result *) plan)->resconstantqual;
+
+               if (list_length(rcqual) == 1)
+               {
+                       Const      *constqual = (Const *) linitial(rcqual);
+
+                       if (constqual && IsA(constqual, Const))
+                       {
+                               if (!constqual->constisnull &&
+                                       !DatumGetBool(constqual->constvalue))
+                                       return true;
+                       }
+               }
+       }
+       return false;
+}
+
 /*
  * preprocess_limit - do pre-estimation for LIMIT and/or OFFSET clauses
  *
@@ -1249,13 +1346,42 @@ preprocess_limit(PlannerInfo *root, double tuple_fraction,
        return tuple_fraction;
 }
 
+/*
+ * extract_grouping_ops - make an array of the equality operator OIDs
+ *             for the GROUP BY clause
+ */
+static Oid *
+extract_grouping_ops(List *groupClause)
+{
+       int                     numCols = list_length(groupClause);
+       int                     colno = 0;
+       Oid                *groupOperators;
+       ListCell   *glitem;
+
+       groupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+
+       foreach(glitem, groupClause)
+       {
+               GroupClause *groupcl = (GroupClause *) lfirst(glitem);
+
+               groupOperators[colno] = get_equality_op_for_ordering_op(groupcl->sortop);
+               if (!OidIsValid(groupOperators[colno]))         /* shouldn't happen */
+                       elog(ERROR, "could not find equality operator for ordering operator %u",
+                                groupcl->sortop);
+               colno++;
+       }
+
+       return groupOperators;
+}
+
 /*
  * choose_hashed_grouping - should we use hashed grouping?
  */
 static bool
 choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
                                           Path *cheapest_path, Path *sorted_path,
-                                          double dNumGroups, AggClauseCounts *agg_counts)
+                                          Oid *groupOperators, double dNumGroups,
+                                          AggClauseCounts *agg_counts)
 {
        int                     numGroupCols = list_length(root->parse->groupClause);
        double          cheapest_path_rows;
@@ -1264,10 +1390,13 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
        List       *current_pathkeys;
        Path            hashed_p;
        Path            sorted_p;
+       int                     i;
 
        /*
         * Check can't-do-it conditions, including whether the grouping operators
-        * are hashjoinable.
+        * are hashjoinable.  (We assume hashing is OK if they are marked
+        * oprcanhash.  If there isn't actually a supporting hash function,
+        * the executor will complain at runtime.)
         *
         * Executor doesn't support hashed aggregation with DISTINCT aggregates.
         * (Doing so would imply storing *all* the input values in the hash table,
@@ -1277,8 +1406,11 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
                return false;
        if (agg_counts->numDistinctAggs != 0)
                return false;
-       if (!hash_safe_grouping(root))
-               return false;
+       for (i = 0; i < numGroupCols; i++)
+       {
+               if (!op_hashjoinable(groupOperators[i]))
+                       return false;
+       }
 
        /*
         * Don't do it if it doesn't look like the hashtable will fit into
@@ -1383,36 +1515,6 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
        return false;
 }
 
-/*
- * hash_safe_grouping - are grouping operators hashable?
- *
- * We assume hashed aggregation will work if the datatype's equality operator
- * is marked hashjoinable.
- */
-static bool
-hash_safe_grouping(PlannerInfo *root)
-{
-       ListCell   *gl;
-
-       foreach(gl, root->parse->groupClause)
-       {
-               GroupClause *grpcl = (GroupClause *) lfirst(gl);
-               TargetEntry *tle = get_sortgroupclause_tle(grpcl,
-                                                                                                  root->parse->targetList);
-               Operator        optup;
-               bool            oprcanhash;
-
-               optup = equality_oper(exprType((Node *) tle->expr), true);
-               if (!optup)
-                       return false;
-               oprcanhash = ((Form_pg_operator) GETSTRUCT(optup))->oprcanhash;
-               ReleaseSysCache(optup);
-               if (!oprcanhash)
-                       return false;
-       }
-       return true;
-}
-
 /*---------------
  * make_subplanTargetList
  *       Generate appropriate target list when grouping is required.