Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 658ee01

Browse files
committed
Make executor's SELECT INTO code save and restore original tuple receiver.
As previously coded, the QueryDesc's dest pointer was left dangling (pointing at an already-freed receiver object) after ExecutorEnd. It's a bit astonishing that it took us this long to notice, and I'm not sure that the known problem case with SQL functions is the only one. Fix it by saving and restoring the original receiver pointer, which seems the most bulletproof way of ensuring any related bugs are also covered. Per bug #6379 from Paul Ramsey. Back-patch to 8.4 where the current handling of SELECT INTO was introduced.
1 parent 188f1b9 commit 658ee01

File tree

3 files changed

+55
-5
lines changed

3 files changed

+55
-5
lines changed

src/backend/executor/execMain.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2372,6 +2372,7 @@ typedef struct
23722372
{
23732373
DestReceiver pub; /* publicly-known function pointers */
23742374
EState *estate; /* EState we are working with */
2375+
DestReceiver *origdest; /* QueryDesc's original receiver */
23752376
Relation rel; /* Relation to write to */
23762377
int hi_options; /* heap_insert performance options */
23772378
BulkInsertState bistate; /* bulk insert state */
@@ -2526,12 +2527,14 @@ OpenIntoRel(QueryDesc *queryDesc)
25262527
/*
25272528
* Now replace the query's DestReceiver with one for SELECT INTO
25282529
*/
2529-
queryDesc->dest = CreateDestReceiver(DestIntoRel);
2530-
myState = (DR_intorel *) queryDesc->dest;
2530+
myState = (DR_intorel *) CreateDestReceiver(DestIntoRel);
25312531
Assert(myState->pub.mydest == DestIntoRel);
25322532
myState->estate = estate;
2533+
myState->origdest = queryDesc->dest;
25332534
myState->rel = intoRelationDesc;
25342535

2536+
queryDesc->dest = (DestReceiver *) myState;
2537+
25352538
/*
25362539
* We can skip WAL-logging the insertions, unless PITR or streaming
25372540
* replication is in use. We can skip the FSM in any case.
@@ -2552,8 +2555,11 @@ CloseIntoRel(QueryDesc *queryDesc)
25522555
{
25532556
DR_intorel *myState = (DR_intorel *) queryDesc->dest;
25542557

2555-
/* OpenIntoRel might never have gotten called */
2556-
if (myState && myState->pub.mydest == DestIntoRel && myState->rel)
2558+
/*
2559+
* OpenIntoRel might never have gotten called, and we also want to guard
2560+
* against double destruction.
2561+
*/
2562+
if (myState && myState->pub.mydest == DestIntoRel)
25572563
{
25582564
FreeBulkInsertState(myState->bistate);
25592565

@@ -2564,7 +2570,11 @@ CloseIntoRel(QueryDesc *queryDesc)
25642570
/* close rel, but keep lock until commit */
25652571
heap_close(myState->rel, NoLock);
25662572

2567-
myState->rel = NULL;
2573+
/* restore the receiver belonging to executor's caller */
2574+
queryDesc->dest = myState->origdest;
2575+
2576+
/* might as well invoke my destructor */
2577+
intorel_destroy((DestReceiver *) myState);
25682578
}
25692579
}
25702580

src/test/regress/expected/select_into.out

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,28 @@ SELECT *
1111
FROM onek2
1212
WHERE onek2.unique1 < 2;
1313
DROP TABLE tmp1;
14+
--
15+
-- CREATE TABLE AS/SELECT INTO as last command in a SQL function
16+
-- have been known to cause problems
17+
--
18+
CREATE FUNCTION make_table() RETURNS VOID
19+
AS $$
20+
CREATE TABLE created_table AS SELECT * FROM int8_tbl;
21+
$$ LANGUAGE SQL;
22+
SELECT make_table();
23+
make_table
24+
------------
25+
26+
(1 row)
27+
28+
SELECT * FROM created_table;
29+
q1 | q2
30+
------------------+-------------------
31+
123 | 456
32+
123 | 4567890123456789
33+
4567890123456789 | 123
34+
4567890123456789 | 4567890123456789
35+
4567890123456789 | -4567890123456789
36+
(5 rows)
37+
38+
DROP TABLE created_table;

src/test/regress/sql/select_into.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,18 @@ SELECT *
1515
WHERE onek2.unique1 < 2;
1616

1717
DROP TABLE tmp1;
18+
19+
--
20+
-- CREATE TABLE AS/SELECT INTO as last command in a SQL function
21+
-- have been known to cause problems
22+
--
23+
CREATE FUNCTION make_table() RETURNS VOID
24+
AS $$
25+
CREATE TABLE created_table AS SELECT * FROM int8_tbl;
26+
$$ LANGUAGE SQL;
27+
28+
SELECT make_table();
29+
30+
SELECT * FROM created_table;
31+
32+
DROP TABLE created_table;

0 commit comments

Comments
 (0)