diff options
| author | Tom Lane | 2012-09-05 16:54:03 +0000 |
|---|---|---|
| committer | Tom Lane | 2012-09-05 16:55:01 +0000 |
| commit | 46c508fbcf98ac334f1e831d21021d731c882fbb (patch) | |
| tree | a2b74ff26e8e5d5f3fc899e1ab6bf68da635b96e /src/test | |
| parent | e20a90e1887152f1e0c149a02c50d6bafb2596e5 (diff) | |
Fix PARAM_EXEC assignment mechanism to be safe in the presence of WITH.
The planner previously assumed that parameter Vars having the same absolute
query level, varno, and varattno could safely be assigned the same runtime
PARAM_EXEC slot, even though they might be different Vars appearing in
different subqueries. This was (probably) safe before the introduction of
CTEs, but the lazy-evalution mechanism used for CTEs means that a CTE can
be executed during execution of some other subquery, causing the lifespan
of Params at the same syntactic nesting level as the CTE to overlap with
use of the same slots inside the CTE. In 9.1 we created additional hazards
by using the same parameter-assignment technology for nestloop inner scan
parameters, but it was broken before that, as illustrated by the added
regression test.
To fix, restructure the planner's management of PlannerParamItems so that
items having different semantic lifespans are kept rigorously separated.
This will probably result in complex queries using more runtime PARAM_EXEC
slots than before, but the slots are cheap enough that this hardly matters.
Also, stop generating PlannerParamItems containing Params for subquery
outputs: all we really need to do is reserve the PARAM_EXEC slot number,
and that now only takes incrementing a counter. The planning code is
simpler and probably faster than before, as well as being more correct.
Per report from Vik Reykja.
These changes will mostly also need to be made in the back branches, but
I'm going to hold off on that until after 9.2.0 wraps.
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/regress/expected/with.out | 21 | ||||
| -rw-r--r-- | src/test/regress/sql/with.sql | 14 |
2 files changed, 35 insertions, 0 deletions
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index a7491bf6b93..38cfb8c7276 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -1210,6 +1210,27 @@ ERROR: recursive reference to query "outermost" must not appear within a subque LINE 2: WITH innermost as (SELECT 2 FROM outermost) ^ -- +-- This test will fail with the old implementation of PARAM_EXEC parameter +-- assignment, because the "q1" Var passed down to A's targetlist subselect +-- looks exactly like the "A.id" Var passed down to C's subselect, causing +-- the old code to give them the same runtime PARAM_EXEC slot. But the +-- lifespans of the two parameters overlap, thanks to B also reading A. +-- +with +A as ( select q2 as id, (select q1) as x from int8_tbl ), +B as ( select id, row_number() over (partition by id) as r from A ), +C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) +select * from C; + id | array +-------------------+------------------------------------- + 456 | {456} + 4567890123456789 | {4567890123456789,4567890123456789} + 123 | {123} + 4567890123456789 | {4567890123456789,4567890123456789} + -4567890123456789 | {-4567890123456789} +(5 rows) + +-- -- Test CTEs read in non-initialization orders -- WITH RECURSIVE diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql index 684c8f13db1..609f51b0c95 100644 --- a/src/test/regress/sql/with.sql +++ b/src/test/regress/sql/with.sql @@ -575,6 +575,20 @@ WITH RECURSIVE outermost(x) AS ( SELECT * FROM outermost; -- +-- This test will fail with the old implementation of PARAM_EXEC parameter +-- assignment, because the "q1" Var passed down to A's targetlist subselect +-- looks exactly like the "A.id" Var passed down to C's subselect, causing +-- the old code to give them the same runtime PARAM_EXEC slot. But the +-- lifespans of the two parameters overlap, thanks to B also reading A. +-- + +with +A as ( select q2 as id, (select q1) as x from int8_tbl ), +B as ( select id, row_number() over (partition by id) as r from A ), +C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) +select * from C; + +-- -- Test CTEs read in non-initialization orders -- |
