Fix optimizer to not try to push WHERE clauses down into a sub-SELECT that
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 31 Jul 2001 18:39:13 +0000 (18:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 31 Jul 2001 18:39:13 +0000 (18:39 +0000)
has a DISTINCT ON clause, per bug report from Anthony Wood.  While at it,
improve the DISTINCT-ON-clause recognizer routine to not be fooled by out-
of-order DISTINCT lists.
Also, back-patch earlier fix to not push down into sub-SELECT with LIMIT.

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/util/clauses.c
src/backend/utils/adt/ruleutils.c
src/include/optimizer/clauses.h

index 1cf73dffff769ed7bda494674909cd76726f82d4..820a0e6e26f5da3cf79cd69c47d218ff6536deaa 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.72 2001/03/22 03:59:34 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.72.2.1 2001/07/31 18:39:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -125,11 +125,17 @@ set_base_rel_pathlists(Query *root)
             * Non-pushed-down clauses will get evaluated as qpquals of
             * the SubqueryScan node.
             *
+            * We can't push down into subqueries with LIMIT or DISTINCT ON
+            * clauses, either.
+            *
             * XXX Are there any cases where we want to make a policy
             * decision not to push down, because it'd result in a worse
             * plan?
             */
-           if (rte->subquery->setOperations == NULL)
+           if (rte->subquery->setOperations == NULL &&
+               rte->subquery->limitOffset == NULL &&
+               rte->subquery->limitCount == NULL &&
+               !has_distinct_on_clause(rte->subquery))
            {
                /* OK to consider pushing down individual quals */
                List       *upperrestrictlist = NIL;
index 8bd6ef6f68b23e650f24cf7262713c68314c183f..5e2a7b63dcac97765e92c0ec0c2a48a23a685a4f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.84 2001/03/27 17:12:34 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.84.2.1 2001/07/31 18:39:12 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -729,6 +729,58 @@ pull_constant_clauses(List *quals, List **constantQual)
 }
 
 
+/*****************************************************************************
+ *     Tests on clauses of queries
+ *
+ * Possibly this code should go someplace else, since this isn't quite the
+ * same meaning of "clause" as is used elsewhere in this module.  But I can't
+ * think of a better place for it...
+ *****************************************************************************/
+
+/*
+ * Test whether a query uses DISTINCT ON, ie, has a distinct-list that is
+ * just a subset of the output columns.
+ */
+bool
+has_distinct_on_clause(Query *query)
+{
+   List   *targetList;
+
+   /* Is there a DISTINCT clause at all? */
+   if (query->distinctClause == NIL)
+       return false;
+   /*
+    * If the DISTINCT list contains all the nonjunk targetlist items,
+    * then it's a simple DISTINCT, else it's DISTINCT ON.  We do not
+    * require the lists to be in the same order (since the parser may
+    * have adjusted the DISTINCT clause ordering to agree with ORDER BY).
+    */
+   foreach(targetList, query->targetList)
+   {
+       TargetEntry *tle = (TargetEntry *) lfirst(targetList);
+       Index       ressortgroupref;
+       List       *distinctClause;
+
+       if (tle->resdom->resjunk)
+           continue;
+       ressortgroupref = tle->resdom->ressortgroupref;
+       if (ressortgroupref == 0)
+           return true;        /* definitely not in DISTINCT list */
+       foreach(distinctClause, query->distinctClause)
+       {
+           SortClause *scl = (SortClause *) lfirst(distinctClause);
+
+           if (scl->tleSortGroupRef == ressortgroupref)
+               break;          /* found TLE in DISTINCT */
+       }
+       if (distinctClause == NIL)
+           return true;        /* this TLE is not in DISTINCT list */
+   }
+   /* It's a simple DISTINCT */
+   return false;
+}
+
+
 /*****************************************************************************
  *                                                                          *
  *     General clause-manipulating routines                                 *
@@ -736,7 +788,7 @@ pull_constant_clauses(List *quals, List **constantQual)
  *****************************************************************************/
 
 /*
- * clause_relids_vars
+ * clause_get_relids_vars
  *   Retrieves distinct relids and vars appearing within a clause.
  *
  * '*relids' is set to an integer list of all distinct "varno"s appearing
index 5635b90a9fb8ad7e0dd997bccb9ffcf84dcb5a52..47d8095494152c9ee5ed5e8029f927c707c1a93e 100644 (file)
@@ -3,7 +3,7 @@
  *             back to source text
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.77 2001/04/18 17:04:24 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.77.2.1 2001/07/31 18:39:12 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -119,7 +119,6 @@ static void get_utility_query_def(Query *query, deparse_context *context);
 static void get_basic_select_query(Query *query, deparse_context *context);
 static void get_setop_query(Node *setOp, Query *query,
                deparse_context *context, bool toplevel);
-static bool simple_distinct(List *distinctClause, List *targetList);
 static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
                                     bool force_colno,
                                     deparse_context *context);
@@ -1006,9 +1005,7 @@ get_basic_select_query(Query *query, deparse_context *context)
    /* Add the DISTINCT clause if given */
    if (query->distinctClause != NIL)
    {
-       if (simple_distinct(query->distinctClause, query->targetList))
-           appendStringInfo(buf, " DISTINCT");
-       else
+       if (has_distinct_on_clause(query))
        {
            appendStringInfo(buf, " DISTINCT ON (");
            sep = "";
@@ -1023,6 +1020,8 @@ get_basic_select_query(Query *query, deparse_context *context)
            }
            appendStringInfo(buf, ")");
        }
+       else
+           appendStringInfo(buf, " DISTINCT");
    }
 
    /* Then we tell what to select (the targetlist) */
@@ -1149,34 +1148,6 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
    }
 }
 
-/*
- * Detect whether a DISTINCT list can be represented as just DISTINCT
- * or needs DISTINCT ON.  It's simple if it contains exactly the nonjunk
- * targetlist items.
- */
-static bool
-simple_distinct(List *distinctClause, List *targetList)
-{
-   while (targetList)
-   {
-       TargetEntry *tle = (TargetEntry *) lfirst(targetList);
-
-       if (!tle->resdom->resjunk)
-       {
-           if (distinctClause == NIL)
-               return false;
-           if (((SortClause *) lfirst(distinctClause))->tleSortGroupRef !=
-               tle->resdom->ressortgroupref)
-               return false;
-           distinctClause = lnext(distinctClause);
-       }
-       targetList = lnext(targetList);
-   }
-   if (distinctClause != NIL)
-       return false;
-   return true;
-}
-
 /*
  * Display a sort/group clause.
  */
index a02ef9c77c30d432e4688071b200c652a179ac26..92487de2ea0ade5ca0fc356164d1f73ee0b016df 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.43 2001/03/22 04:00:53 momjian Exp $
+ * $Id: clauses.h,v 1.43.2.1 2001/07/31 18:39:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,6 +59,8 @@ extern bool contain_noncachable_functions(Node *clause);
 extern bool is_pseudo_constant_clause(Node *clause);
 extern List *pull_constant_clauses(List *quals, List **constantQual);
 
+extern bool has_distinct_on_clause(Query *query);
+
 extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
 extern int NumRelids(Node *clause);
 extern void get_relattval(Node *clause, int targetrelid,