diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index cdff123828172b01335aa2e4d11b5003ea768f6c..f8d11a4714f89f236b6e9401bae7858c85d10257 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.150 2009/06/11 14:48:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.151 2009/07/06 02:16:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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 4a2f18c9e9beb1a0b784e10e433c0f70ba776098..98003ebe6c68d11eff7f1824df5428408d20a77c 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 c736441f5365dbd41e977c12c7f38ea58d1db153..6eaa168d964db3355350e863a4f17c97f7d15caf 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;