summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2009-07-06 02:16:03 +0000
committerTom Lane2009-07-06 02:16:03 +0000
commit3bb47cc1fcc49a9990b9aa3a08fb593f0e408ac6 (patch)
tree34d443f2ab24505b38c50e8394e15580f85399f9
parent2527a1d4e609739130f3a8e5ac543a757b5ea440 (diff)
Fix handling of changed-Param signaling for CteScan plan nodes. We were using
the "cteParam" as a proxy for the possibility that the underlying CTE plan depends on outer-level variables or Params, but that doesn't work very well because it sometimes causes calling subqueries to be treated as SubPlans when they could be InitPlans. This is inefficient and also causes the outright failure exhibited in bug #4902. Instead, leave the cteParam out of it and copy the underlying CTE plan's extParams directly. Per bug #4902 from Marko Tiikkaja.
-rw-r--r--src/backend/optimizer/plan/subselect.c32
-rw-r--r--src/test/regress/expected/with.out41
-rw-r--r--src/test/regress/sql/with.sql16
3 files changed, 86 insertions, 3 deletions
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 58cb6fa831..d670eb1b2d 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1916,9 +1916,35 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params)
break;
case T_CteScan:
- context.paramids =
- bms_add_member(context.paramids,
- ((CteScan *) plan)->cteParam);
+ {
+ /*
+ * You might think we should add the node's cteParam to
+ * paramids, but we shouldn't because that param is just a
+ * linkage mechanism for multiple CteScan nodes for the same
+ * CTE; it is never used for changed-param signaling. What
+ * we have to do instead is to find the referenced CTE plan
+ * and incorporate its external paramids, so that the correct
+ * things will happen if the CTE references outer-level
+ * variables. See test cases for bug #4902.
+ */
+ int plan_id = ((CteScan *) plan)->ctePlanId;
+ Plan *cteplan;
+
+ /* so, do this ... */
+ if (plan_id < 1 || plan_id > list_length(root->glob->subplans))
+ elog(ERROR, "could not find plan for CteScan referencing plan ID %d",
+ plan_id);
+ cteplan = (Plan *) list_nth(root->glob->subplans, plan_id - 1);
+ context.paramids =
+ bms_add_members(context.paramids, cteplan->extParam);
+
+#ifdef NOT_USED
+ /* ... but not this */
+ context.paramids =
+ bms_add_member(context.paramids,
+ ((CteScan *) plan)->cteParam);
+#endif
+ }
break;
case T_WorkTableScan:
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 4a2f18c9e9..98003ebe6c 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -912,3 +912,44 @@ ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive te
LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i)
^
HINT: Cast the output of the non-recursive term to the correct type.
+--
+-- test for bug #4902
+--
+with cte(foo) as ( values(42) ) values((select foo from cte));
+ column1
+---------
+ 42
+(1 row)
+
+with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q;
+ foo
+-----
+ 42
+(1 row)
+
+-- test CTE referencing an outer-level variable (to see that changed-parameter
+-- signaling still works properly after fixing this bug)
+select ( with cte(foo) as ( values(f1) )
+ select (select foo from cte) )
+from int4_tbl;
+ ?column?
+-------------
+ 0
+ 123456
+ -123456
+ 2147483647
+ -2147483647
+(5 rows)
+
+select ( with cte(foo) as ( values(f1) )
+ values((select foo from cte)) )
+from int4_tbl;
+ ?column?
+-------------
+ 0
+ 123456
+ -123456
+ 2147483647
+ -2147483647
+(5 rows)
+
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index c736441f53..6eaa168d96 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -469,3 +469,19 @@ WITH RECURSIVE foo(i) AS
UNION ALL
SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10)
SELECT * FROM foo;
+
+--
+-- test for bug #4902
+--
+with cte(foo) as ( values(42) ) values((select foo from cte));
+with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q;
+
+-- test CTE referencing an outer-level variable (to see that changed-parameter
+-- signaling still works properly after fixing this bug)
+select ( with cte(foo) as ( values(f1) )
+ select (select foo from cte) )
+from int4_tbl;
+
+select ( with cte(foo) as ( values(f1) )
+ values((select foo from cte)) )
+from int4_tbl;