mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-12 18:34:36 +08:00
Fix corner case wherein a WorkTableScan node could get initialized before the
RecursiveUnion to which it refers. It turns out that we can just postpone the relevant initialization steps until the first exec call for the node, by which time the ancestor node must surely be initialized. Per report from Greg Stark.
This commit is contained in:
parent
100aa2795d
commit
0a7abcd4c9
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/nodeWorktablescan.c,v 1.1 2008/10/04 21:56:53 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/nodeWorktablescan.c,v 1.2 2008/10/13 00:41:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -63,6 +63,40 @@ WorkTableScanNext(WorkTableScanState *node)
|
|||||||
TupleTableSlot *
|
TupleTableSlot *
|
||||||
ExecWorkTableScan(WorkTableScanState *node)
|
ExecWorkTableScan(WorkTableScanState *node)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* On the first call, find the ancestor RecursiveUnion's state
|
||||||
|
* via the Param slot reserved for it. (We can't do this during node
|
||||||
|
* init because there are corner cases where we'll get the init call
|
||||||
|
* before the RecursiveUnion does.)
|
||||||
|
*/
|
||||||
|
if (node->rustate == NULL)
|
||||||
|
{
|
||||||
|
WorkTableScan *plan = (WorkTableScan *) node->ss.ps.plan;
|
||||||
|
EState *estate = node->ss.ps.state;
|
||||||
|
ParamExecData *param;
|
||||||
|
|
||||||
|
param = &(estate->es_param_exec_vals[plan->wtParam]);
|
||||||
|
Assert(param->execPlan == NULL);
|
||||||
|
Assert(!param->isnull);
|
||||||
|
node->rustate = (RecursiveUnionState *) DatumGetPointer(param->value);
|
||||||
|
Assert(node->rustate && IsA(node->rustate, RecursiveUnionState));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The scan tuple type (ie, the rowtype we expect to find in the work
|
||||||
|
* table) is the same as the result rowtype of the ancestor
|
||||||
|
* RecursiveUnion node. Note this depends on the assumption that
|
||||||
|
* RecursiveUnion doesn't allow projection.
|
||||||
|
*/
|
||||||
|
ExecAssignScanType(&node->ss,
|
||||||
|
ExecGetResultType(&node->rustate->ps));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we can initialize the projection info. This must be
|
||||||
|
* completed before we can call ExecScan().
|
||||||
|
*/
|
||||||
|
ExecAssignScanProjectionInfo(&node->ss);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* use WorkTableScanNext as access method
|
* use WorkTableScanNext as access method
|
||||||
*/
|
*/
|
||||||
@ -78,7 +112,6 @@ WorkTableScanState *
|
|||||||
ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
|
ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
|
||||||
{
|
{
|
||||||
WorkTableScanState *scanstate;
|
WorkTableScanState *scanstate;
|
||||||
ParamExecData *prmdata;
|
|
||||||
|
|
||||||
/* check for unsupported flags */
|
/* check for unsupported flags */
|
||||||
Assert(!(eflags & EXEC_FLAG_MARK));
|
Assert(!(eflags & EXEC_FLAG_MARK));
|
||||||
@ -95,16 +128,7 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
|
|||||||
scanstate = makeNode(WorkTableScanState);
|
scanstate = makeNode(WorkTableScanState);
|
||||||
scanstate->ss.ps.plan = (Plan *) node;
|
scanstate->ss.ps.plan = (Plan *) node;
|
||||||
scanstate->ss.ps.state = estate;
|
scanstate->ss.ps.state = estate;
|
||||||
|
scanstate->rustate = NULL; /* we'll set this later */
|
||||||
/*
|
|
||||||
* Find the ancestor RecursiveUnion's state
|
|
||||||
* via the Param slot reserved for it.
|
|
||||||
*/
|
|
||||||
prmdata = &(estate->es_param_exec_vals[node->wtParam]);
|
|
||||||
Assert(prmdata->execPlan == NULL);
|
|
||||||
Assert(!prmdata->isnull);
|
|
||||||
scanstate->rustate = (RecursiveUnionState *) DatumGetPointer(prmdata->value);
|
|
||||||
Assert(scanstate->rustate && IsA(scanstate->rustate, RecursiveUnionState));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Miscellaneous initialization
|
* Miscellaneous initialization
|
||||||
@ -132,19 +156,9 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
|
|||||||
ExecInitScanTupleSlot(estate, &scanstate->ss);
|
ExecInitScanTupleSlot(estate, &scanstate->ss);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The scan tuple type (ie, the rowtype we expect to find in the work
|
* Initialize result tuple type, but not yet projection info.
|
||||||
* table) is the same as the result rowtype of the ancestor RecursiveUnion
|
|
||||||
* node. Note this depends on the assumption that RecursiveUnion doesn't
|
|
||||||
* allow projection.
|
|
||||||
*/
|
|
||||||
ExecAssignScanType(&scanstate->ss,
|
|
||||||
ExecGetResultType(&scanstate->rustate->ps));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize result tuple type and projection info.
|
|
||||||
*/
|
*/
|
||||||
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
|
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
|
||||||
ExecAssignScanProjectionInfo(&scanstate->ss);
|
|
||||||
|
|
||||||
scanstate->ss.ps.ps_TupFromTlist = false;
|
scanstate->ss.ps.ps_TupFromTlist = false;
|
||||||
|
|
||||||
@ -190,5 +204,7 @@ void
|
|||||||
ExecWorkTableScanReScan(WorkTableScanState *node, ExprContext *exprCtxt)
|
ExecWorkTableScanReScan(WorkTableScanState *node, ExprContext *exprCtxt)
|
||||||
{
|
{
|
||||||
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
||||||
tuplestore_rescan(node->rustate->working_table);
|
/* No need (or way) to rescan if ExecWorkTableScan not called yet */
|
||||||
|
if (node->rustate)
|
||||||
|
tuplestore_rescan(node->rustate->working_table);
|
||||||
}
|
}
|
||||||
|
@ -292,6 +292,89 @@ SELECT pg_get_viewdef('vsubdepartment'::regclass, true);
|
|||||||
FROM subdepartment;
|
FROM subdepartment;
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- corner case in which sub-WITH gets initialized first
|
||||||
|
with recursive q as (
|
||||||
|
select * from department
|
||||||
|
union all
|
||||||
|
(with x as (select * from q)
|
||||||
|
select * from x)
|
||||||
|
)
|
||||||
|
select * from q limit 24;
|
||||||
|
id | parent_department | name
|
||||||
|
----+-------------------+------
|
||||||
|
0 | | ROOT
|
||||||
|
1 | 0 | A
|
||||||
|
2 | 1 | B
|
||||||
|
3 | 2 | C
|
||||||
|
4 | 2 | D
|
||||||
|
5 | 0 | E
|
||||||
|
6 | 4 | F
|
||||||
|
7 | 5 | G
|
||||||
|
0 | | ROOT
|
||||||
|
1 | 0 | A
|
||||||
|
2 | 1 | B
|
||||||
|
3 | 2 | C
|
||||||
|
4 | 2 | D
|
||||||
|
5 | 0 | E
|
||||||
|
6 | 4 | F
|
||||||
|
7 | 5 | G
|
||||||
|
0 | | ROOT
|
||||||
|
1 | 0 | A
|
||||||
|
2 | 1 | B
|
||||||
|
3 | 2 | C
|
||||||
|
4 | 2 | D
|
||||||
|
5 | 0 | E
|
||||||
|
6 | 4 | F
|
||||||
|
7 | 5 | G
|
||||||
|
(24 rows)
|
||||||
|
|
||||||
|
with recursive q as (
|
||||||
|
select * from department
|
||||||
|
union all
|
||||||
|
(with recursive x as (
|
||||||
|
select * from department
|
||||||
|
union all
|
||||||
|
(select * from q union all select * from x)
|
||||||
|
)
|
||||||
|
select * from x)
|
||||||
|
)
|
||||||
|
select * from q limit 32;
|
||||||
|
id | parent_department | name
|
||||||
|
----+-------------------+------
|
||||||
|
0 | | ROOT
|
||||||
|
1 | 0 | A
|
||||||
|
2 | 1 | B
|
||||||
|
3 | 2 | C
|
||||||
|
4 | 2 | D
|
||||||
|
5 | 0 | E
|
||||||
|
6 | 4 | F
|
||||||
|
7 | 5 | G
|
||||||
|
0 | | ROOT
|
||||||
|
1 | 0 | A
|
||||||
|
2 | 1 | B
|
||||||
|
3 | 2 | C
|
||||||
|
4 | 2 | D
|
||||||
|
5 | 0 | E
|
||||||
|
6 | 4 | F
|
||||||
|
7 | 5 | G
|
||||||
|
0 | | ROOT
|
||||||
|
1 | 0 | A
|
||||||
|
2 | 1 | B
|
||||||
|
3 | 2 | C
|
||||||
|
4 | 2 | D
|
||||||
|
5 | 0 | E
|
||||||
|
6 | 4 | F
|
||||||
|
7 | 5 | G
|
||||||
|
0 | | ROOT
|
||||||
|
1 | 0 | A
|
||||||
|
2 | 1 | B
|
||||||
|
3 | 2 | C
|
||||||
|
4 | 2 | D
|
||||||
|
5 | 0 | E
|
||||||
|
6 | 4 | F
|
||||||
|
7 | 5 | G
|
||||||
|
(32 rows)
|
||||||
|
|
||||||
-- recursive term has sub-UNION
|
-- recursive term has sub-UNION
|
||||||
WITH RECURSIVE t(i,j) AS (
|
WITH RECURSIVE t(i,j) AS (
|
||||||
VALUES (1,2)
|
VALUES (1,2)
|
||||||
|
@ -178,6 +178,27 @@ SELECT * FROM vsubdepartment ORDER BY name;
|
|||||||
SELECT pg_get_viewdef('vsubdepartment'::regclass);
|
SELECT pg_get_viewdef('vsubdepartment'::regclass);
|
||||||
SELECT pg_get_viewdef('vsubdepartment'::regclass, true);
|
SELECT pg_get_viewdef('vsubdepartment'::regclass, true);
|
||||||
|
|
||||||
|
-- corner case in which sub-WITH gets initialized first
|
||||||
|
with recursive q as (
|
||||||
|
select * from department
|
||||||
|
union all
|
||||||
|
(with x as (select * from q)
|
||||||
|
select * from x)
|
||||||
|
)
|
||||||
|
select * from q limit 24;
|
||||||
|
|
||||||
|
with recursive q as (
|
||||||
|
select * from department
|
||||||
|
union all
|
||||||
|
(with recursive x as (
|
||||||
|
select * from department
|
||||||
|
union all
|
||||||
|
(select * from q union all select * from x)
|
||||||
|
)
|
||||||
|
select * from x)
|
||||||
|
)
|
||||||
|
select * from q limit 32;
|
||||||
|
|
||||||
-- recursive term has sub-UNION
|
-- recursive term has sub-UNION
|
||||||
WITH RECURSIVE t(i,j) AS (
|
WITH RECURSIVE t(i,j) AS (
|
||||||
VALUES (1,2)
|
VALUES (1,2)
|
||||||
|
Loading…
Reference in New Issue
Block a user