Suppress subquery pullup and pushdown when the subquery has any
authorTom Lane <[email protected]>
Mon, 10 Dec 2001 22:54:12 +0000 (22:54 +0000)
committerTom Lane <[email protected]>
Mon, 10 Dec 2001 22:54:12 +0000 (22:54 +0000)
set-returning functions in its target list.  This ensures that we
won't rewrite the query in a way that places set-returning functions
into quals (WHERE clauses).  Cf. bug reports from Joe Conway.

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/util/clauses.c
src/include/optimizer/clauses.h

index 5f8bec2d2b39c8b5fa699bf91ff90ed875f06ea2..d11c6d9ef8de845614b7e7a611b70caa7f138e02 100644 (file)
@@ -305,7 +305,14 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
         * checking that seems more work than it's worth.  In any case, a
         * plain DISTINCT is safe to push down past.)
         *
-        * 3. We do not push down clauses that contain subselects, mainly because
+        * 3. If the subquery has any ITER nodes (ie, functions returning sets)
+        * in its target list, we do not push down any quals, since the quals
+        * might refer to those tlist items, which would mean we'd introduce
+        * functions-returning-sets into the subquery's WHERE/HAVING quals.
+        * (It'd be sufficient to not push down quals that refer to those
+        * particular tlist items, but that's much clumsier to check.)
+        *
+        * 4. We do not push down clauses that contain subselects, mainly because
         * I'm not sure it will work correctly (the subplan hasn't yet
         * transformed sublinks to subselects).
         *
@@ -318,7 +325,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
        if (subquery->setOperations == NULL &&
                subquery->limitOffset == NULL &&
                subquery->limitCount == NULL &&
-               !has_distinct_on_clause(subquery))
+               !has_distinct_on_clause(subquery) &&
+               !contain_iter_clause((Node *) subquery->targetList))
        {
                /* OK to consider pushing down individual quals */
                List       *upperrestrictlist = NIL;
index 46d9213f0fb24abf5bdc4e0d6401b77b17ad32bd..20ec104997aa8ec70e8e1aeafc888642e8b24c5a 100644 (file)
@@ -461,6 +461,15 @@ is_simple_subquery(Query *subquery)
                subquery->limitCount)
                return false;
 
+       /*
+        * Don't pull up a subquery that has any set-returning functions in
+        * its targetlist.  Otherwise we might well wind up inserting
+        * set-returning functions into places where they mustn't go,
+        * such as quals of higher queries.
+        */
+       if (contain_iter_clause((Node *) subquery->targetList))
+               return false;
+
        /*
         * Hack: don't try to pull up a subquery with an empty jointree.
         * query_planner() will correctly generate a Result plan for a
index cad357589f163b7a3d197f10f1ed58dd41935124..e10017f08a5c2264b920ca2cea1fc297e01fe987 100644 (file)
@@ -47,6 +47,7 @@ typedef struct
 
 static bool contain_agg_clause_walker(Node *node, void *context);
 static bool pull_agg_clause_walker(Node *node, List **listptr);
+static bool contain_iter_clause_walker(Node *node, void *context);
 static bool contain_subplans_walker(Node *node, void *context);
 static bool pull_subplans_walker(Node *node, List **listptr);
 static bool check_subplans_for_ungrouped_vars_walker(Node *node,
@@ -450,6 +451,39 @@ pull_agg_clause_walker(Node *node, List **listptr)
 }
 
 
+/*****************************************************************************
+ *             Iter clause manipulation
+ *****************************************************************************/
+
+/*
+ * contain_iter_clause
+ *       Recursively search for Iter nodes within a clause.
+ *
+ *       Returns true if any Iter found.
+ *
+ * XXX Iter is a crock.  It'd be better to look directly at each function
+ * or operator to see if it can return a set.  However, that would require
+ * a lot of extra cycles as things presently stand.  The return-type info
+ * for function and operator nodes should be extended to include whether
+ * the return is a set.
+ */
+bool
+contain_iter_clause(Node *clause)
+{
+       return contain_iter_clause_walker(clause, NULL);
+}
+
+static bool
+contain_iter_clause_walker(Node *node, void *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Iter))
+               return true;                    /* abort the tree traversal and return
+                                                                * true */
+       return expression_tree_walker(node, contain_iter_clause_walker, context);
+}
+
 /*****************************************************************************
  *             Subplan clause manipulation
  *****************************************************************************/
index 892211a0c19bc28875e12642de5f94d5627da8a5..9380a50c7cfbda5950806eaf80f78a73270bbe9b 100644 (file)
@@ -42,6 +42,8 @@ extern List *make_ands_implicit(Expr *clause);
 extern bool contain_agg_clause(Node *clause);
 extern List *pull_agg_clause(Node *clause);
 
+extern bool contain_iter_clause(Node *clause);
+
 extern bool contain_subplans(Node *clause);
 extern List *pull_subplans(Node *clause);
 extern void check_subplans_for_ungrouped_vars(Query *query);