summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2010-07-08 00:14:41 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2010-07-08 00:14:41 +0000
commit1cf269cf99b59da9693d95f1c66bccdce11fac4c (patch)
tree08d6dbf36821bc318857137220342478ac6ad50d
parent97f29c865f4332d48465735b22a42d9b0deacd12 (diff)
downloadpostgresql-1cf269cf99b59da9693d95f1c66bccdce11fac4c.tar.gz
Fix "cannot handle unplanned sub-select" error that can occur when a
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.
-rw-r--r--src/backend/optimizer/util/var.c30
-rw-r--r--src/test/regress/expected/subselect.out14
-rw-r--r--src/test/regress/sql/subselect.sql12
3 files changed, 54 insertions, 2 deletions
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index b5e6417f1c..91aac8f4d7 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.54 2003/08/08 21:41:55 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.54.4.1 2010/07/08 00:14:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -51,6 +51,8 @@ typedef struct
{
Query *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,
@@ -488,6 +490,14 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
* relation variables instead. This allows quals involving such vars to be
* pushed down.
*
+ * 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.
*/
@@ -498,6 +508,10 @@ flatten_join_alias_vars(Query *root, Node *node)
context.root = root;
context.sublevels_up = 0;
+ /* flag whether join aliases could possibly contain SubLinks */
+ context.possible_sublink = root->hasSubLinks;
+ /* if hasSubLinks is already true, no need to work hard */
+ context.inserted_sublink = root->hasSubLinks;
return flatten_join_alias_vars_mutator(node, &context);
}
@@ -533,8 +547,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))
{
@@ -559,12 +580,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;
}
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index f7b8297809..8ee0d8f5fa 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -201,3 +201,17 @@ select count(distinct ss.ten) from
10
(1 row)
+--
+-- 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)
+
diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql
index 5ff9d1cf87..5b0c9365cb 100644
--- a/src/test/regress/sql/subselect.sql
+++ b/src/test/regress/sql/subselect.sql
@@ -93,3 +93,15 @@ select count(*) from
select count(distinct ss.ten) from
(select ten from tenk1 a
where unique1 IN (select distinct hundred from tenk1 b)) ss;
+
+--
+-- 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;