Fix "cannot handle unplanned sub-select" error that can occur when a
authorTom Lane <[email protected]>
Thu, 8 Jul 2010 00:14:28 +0000 (00:14 +0000)
committerTom Lane <[email protected]>
Thu, 8 Jul 2010 00:14:28 +0000 (00:14 +0000)
sub-select contains a join alias reference that expands into an expression
containing another sub-select.  Per yesterday's report from Merlin Moncure
and subsequent off-list investigation.

Back-patch to 7.4.  Older versions didn't attempt to flatten sub-selects in
ways that would trigger this problem.

src/backend/optimizer/util/var.c
src/test/regress/expected/subselect.out
src/test/regress/sql/subselect.sql

index dc1004cbd0ea730bf510e90fb8e6205d038e7b40..a76f1677e49db0859480b20e92c9ddd7e41d32ba 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.66 2005/10/15 02:49:21 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.66.2.1 2010/07/08 00:14:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,6 +51,8 @@ typedef struct
 {
    PlannerInfo *root;
    int         sublevels_up;
+   bool        possible_sublink;       /* could aliases include a SubLink? */
+   bool        inserted_sublink;       /* have we inserted a SubLink? */
 } flatten_join_alias_vars_context;
 
 static bool pull_varnos_walker(Node *node,
@@ -476,6 +478,14 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
  *   is necessary since we will not scan the JOIN as a base relation, which
  *   is the only way that the executor can directly handle whole-row Vars.
  *
+ * If a JOIN contains sub-selects that have been flattened, its join alias
+ * entries might now be arbitrary expressions, not just Vars.  This affects
+ * this function in one important way: we might find ourselves inserting
+ * SubLink expressions into subqueries, and we must make sure that their
+ * Query.hasSubLinks fields get set to TRUE if so.  If there are any
+ * SubLinks in the join alias lists, the outer Query should already have
+ * hasSubLinks = TRUE, so this is only relevant to un-flattened subqueries.
+ *
  * NOTE: this is used on not-yet-planned expressions.  We do not expect it
  * to be applied directly to a Query node.
  */
@@ -486,6 +496,10 @@ flatten_join_alias_vars(PlannerInfo *root, Node *node)
 
    context.root = root;
    context.sublevels_up = 0;
+   /* flag whether join aliases could possibly contain SubLinks */
+   context.possible_sublink = root->parse->hasSubLinks;
+   /* if hasSubLinks is already true, no need to work hard */
+   context.inserted_sublink = root->parse->hasSubLinks;
 
    return flatten_join_alias_vars_mutator(node, &context);
 }
@@ -535,6 +549,7 @@ flatten_join_alias_vars_mutator(Node *node,
                    IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
                }
                /* Recurse in case join input is itself a join */
+               /* (also takes care of setting inserted_sublink if needed) */
                newvar = flatten_join_alias_vars_mutator(newvar, context);
                fields = lappend(fields, newvar);
            }
@@ -559,8 +574,15 @@ flatten_join_alias_vars_mutator(Node *node,
            newvar = copyObject(newvar);
            IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
        }
+
        /* Recurse in case join input is itself a join */
-       return flatten_join_alias_vars_mutator(newvar, context);
+       newvar = flatten_join_alias_vars_mutator(newvar, context);
+
+       /* Detect if we are adding a sublink to query */
+       if (context->possible_sublink && !context->inserted_sublink)
+           context->inserted_sublink = checkExprHasSubLink(newvar);
+
+       return newvar;
    }
    if (IsA(node, InClauseInfo))
    {
@@ -585,12 +607,17 @@ flatten_join_alias_vars_mutator(Node *node,
    {
        /* Recurse into RTE subquery or not-yet-planned sublink subquery */
        Query      *newnode;
+       bool        save_inserted_sublink;
 
        context->sublevels_up++;
+       save_inserted_sublink = context->inserted_sublink;
+       context->inserted_sublink = ((Query *) node)->hasSubLinks;
        newnode = query_tree_mutator((Query *) node,
                                     flatten_join_alias_vars_mutator,
                                     (void *) context,
                                     QTW_IGNORE_JOINALIASES);
+       newnode->hasSubLinks |= context->inserted_sublink;
+       context->inserted_sublink = save_inserted_sublink;
        context->sublevels_up--;
        return (Node *) newnode;
    }
index bfddea582f0aea1dd674fa8733da12625cc824c4..339b46f29729d5d6cb65ef982f04d38e5d7f5871 100644 (file)
@@ -435,3 +435,17 @@ from tc;
          3
 (2 rows)
 
+--
+-- Test case for sublinks pushed down into subselects via join alias expansion
+--
+select
+  (select sq1) as qq1
+from
+  (select exists(select 1 from int4_tbl where f1 = q2) as sq1, 42 as dummy
+   from int8_tbl) sq0
+  join
+  int4_tbl i4 on dummy = i4.f1;
+ qq1 
+-----
+(0 rows)
+
index 2fc947d85426636d5dd1465f139b60e0f5ee71fe..46b46b4c45e160065ceafed0a687bcd9d8a65447 100644 (file)
@@ -277,3 +277,15 @@ select
   ( select min(tb.id) from tb
     where tb.aval = (select ta.val from ta where ta.id = tc.aid) ) as min_tb_id
 from tc;
+
+--
+-- Test case for sublinks pushed down into subselects via join alias expansion
+--
+
+select
+  (select sq1) as qq1
+from
+  (select exists(select 1 from int4_tbl where f1 = q2) as sq1, 42 as dummy
+   from int8_tbl) sq0
+  join
+  int4_tbl i4 on dummy = i4.f1;