Fix oversight in MIN/MAX optimization: must not return NULL entries
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Apr 2005 05:11:28 +0000 (05:11 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Apr 2005 05:11:28 +0000 (05:11 +0000)
from index, since the aggregates ignore NULLs.

src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planagg.c
src/include/optimizer/planmain.h

index 3774af61b4c77f6f711849994578099fecfbbd49..173f92494f4f1b93c39281e70c169e20de5d157b 100644 (file)
@@ -73,7 +73,6 @@ static void fix_indxqual_sublist(List *indexqual, IndexOptInfo *index,
 static Node *fix_indxqual_operand(Node *node, IndexOptInfo *index,
                                                                  Oid *opclass);
 static List *get_switched_clauses(List *clauses, Relids outerrelids);
-static List *order_qual_clauses(Query *root, List *clauses);
 static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
@@ -1417,7 +1416,7 @@ get_switched_clauses(List *clauses, Relids outerrelids)
  * For now, we just move any quals that contain SubPlan references (but not
  * InitPlan references) to the end of the list.
  */
-static List *
+List *
 order_qual_clauses(Query *root, List *clauses)
 {
        List       *nosubplans;
index 324cf7b844799c5c8db4c6c4649b1a9a5eafb472..eda2628478440b822da0056da0ff3f91eed7d812 100644 (file)
@@ -185,6 +185,8 @@ optimize_minmax_aggregates(Query *root, List *tlist, Path *best_path)
        {
                Assert(((ResultPath *) best_path)->subpath != NULL);
                constant_quals = ((ResultPath *) best_path)->constantqual;
+               /* no need to do this more than once: */
+               constant_quals = order_qual_clauses(root, constant_quals);
        }
        else
                constant_quals = NIL;
@@ -438,10 +440,10 @@ static void
 make_agg_subplan(Query *root, MinMaxAggInfo *info, List *constant_quals)
 {
        Query      *subquery;
-       Path       *path;
        Plan       *plan;
        TargetEntry *tle;
        SortClause *sortcl;
+       NullTest   *ntest;
 
        /*
         * Generate a suitably modified Query node.  Much of the work here is
@@ -482,18 +484,30 @@ make_agg_subplan(Query *root, MinMaxAggInfo *info, List *constant_quals)
         * Generate the plan for the subquery.  We already have a Path for
         * the basic indexscan, but we have to convert it to a Plan and
         * attach a LIMIT node above it.  We might need a gating Result, too,
-        * which is most easily added at the Path stage.
+        * to handle any non-variable qual clauses.
+        *
+        * Also we must add a "WHERE foo IS NOT NULL" restriction to the
+        * indexscan, to be sure we don't return a NULL, which'd be contrary
+        * to the standard behavior of MIN/MAX.  XXX ideally this should be
+        * done earlier, so that the selectivity of the restriction could be
+        * included in our cost estimates.  But that looks painful, and in
+        * most cases the fraction of NULLs isn't high enough to change the
+        * decision.
         */
-       path = (Path *) info->path;
+       plan = create_plan(subquery, (Path *) info->path);
 
-       if (constant_quals)
-               path = (Path *) create_result_path(NULL,
-                                                                                  path,
-                                                                                  copyObject(constant_quals));
+       plan->targetlist = copyObject(subquery->targetList);
 
-       plan = create_plan(subquery, path);
+       ntest = makeNode(NullTest);
+       ntest->nulltesttype = IS_NOT_NULL;
+       ntest->arg = copyObject(info->target);
 
-       plan->targetlist = copyObject(subquery->targetList);
+       plan->qual = lappend(plan->qual, ntest);
+
+       if (constant_quals)
+               plan = (Plan *) make_result(copyObject(plan->targetlist),
+                                                                       copyObject(constant_quals),
+                                                                       plan);
 
        plan = (Plan *) make_limit(plan, 
                                                           subquery->limitOffset,
index 93419b0118b2329c3b2b3405a64fcbf050e837ad..9d9e277b42e7335021d0045dd3e547d920a3616c 100644 (file)
@@ -40,6 +40,7 @@ extern Sort *make_sort_from_sortclauses(Query *root, List *sortcls,
                                                   Plan *lefttree);
 extern Sort *make_sort_from_groupcols(Query *root, List *groupcls,
                                                 AttrNumber *grpColIdx, Plan *lefttree);
+extern List *order_qual_clauses(Query *root, List *clauses);
 extern Agg *make_agg(Query *root, List *tlist, List *qual,
                 AggStrategy aggstrategy,
                 int numGroupCols, AttrNumber *grpColIdx,