1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

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.
This commit is contained in:
Tom Lane
2012-01-04 18:31:14 -05:00
parent 9e2b3db158
commit d23c38d46c
3 changed files with 54 additions and 5 deletions

View File

@ -2842,6 +2842,7 @@ typedef struct
{ {
DestReceiver pub; /* publicly-known function pointers */ DestReceiver pub; /* publicly-known function pointers */
EState *estate; /* EState we are working with */ EState *estate; /* EState we are working with */
DestReceiver *origdest; /* QueryDesc's original receiver */
Relation rel; /* Relation to write to */ Relation rel; /* Relation to write to */
int hi_options; /* heap_insert performance options */ int hi_options; /* heap_insert performance options */
BulkInsertState bistate; /* bulk insert state */ BulkInsertState bistate; /* bulk insert state */
@ -2999,12 +3000,14 @@ OpenIntoRel(QueryDesc *queryDesc)
/* /*
* Now replace the query's DestReceiver with one for SELECT INTO * Now replace the query's DestReceiver with one for SELECT INTO
*/ */
queryDesc->dest = CreateDestReceiver(DestIntoRel); myState = (DR_intorel *) CreateDestReceiver(DestIntoRel);
myState = (DR_intorel *) queryDesc->dest;
Assert(myState->pub.mydest == DestIntoRel); Assert(myState->pub.mydest == DestIntoRel);
myState->estate = estate; myState->estate = estate;
myState->origdest = queryDesc->dest;
myState->rel = intoRelationDesc; myState->rel = intoRelationDesc;
queryDesc->dest = (DestReceiver *) myState;
/* /*
* We can skip WAL-logging the insertions, unless PITR is in use. We can * We can skip WAL-logging the insertions, unless PITR is in use. We can
* skip the FSM in any case. * skip the FSM in any case.
@ -3025,8 +3028,11 @@ CloseIntoRel(QueryDesc *queryDesc)
{ {
DR_intorel *myState = (DR_intorel *) queryDesc->dest; DR_intorel *myState = (DR_intorel *) queryDesc->dest;
/* OpenIntoRel might never have gotten called */ /*
if (myState && myState->pub.mydest == DestIntoRel && myState->rel) * OpenIntoRel might never have gotten called, and we also want to guard
* against double destruction.
*/
if (myState && myState->pub.mydest == DestIntoRel)
{ {
FreeBulkInsertState(myState->bistate); FreeBulkInsertState(myState->bistate);
@ -3037,7 +3043,11 @@ CloseIntoRel(QueryDesc *queryDesc)
/* close rel, but keep lock until commit */ /* close rel, but keep lock until commit */
heap_close(myState->rel, NoLock); heap_close(myState->rel, NoLock);
myState->rel = NULL; /* restore the receiver belonging to executor's caller */
queryDesc->dest = myState->origdest;
/* might as well invoke my destructor */
intorel_destroy((DestReceiver *) myState);
} }
} }

View File

@ -11,3 +11,28 @@ SELECT *
FROM onek2 FROM onek2
WHERE onek2.unique1 < 2; WHERE onek2.unique1 < 2;
DROP TABLE tmp1; DROP TABLE tmp1;
--
-- CREATE TABLE AS/SELECT INTO as last command in a SQL function
-- have been known to cause problems
--
CREATE FUNCTION make_table() RETURNS VOID
AS $$
CREATE TABLE created_table AS SELECT * FROM int8_tbl;
$$ LANGUAGE SQL;
SELECT make_table();
make_table
------------
(1 row)
SELECT * FROM created_table;
q1 | q2
------------------+-------------------
123 | 456
123 | 4567890123456789
4567890123456789 | 123
4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789
(5 rows)
DROP TABLE created_table;

View File

@ -16,3 +16,17 @@ SELECT *
DROP TABLE tmp1; DROP TABLE tmp1;
--
-- CREATE TABLE AS/SELECT INTO as last command in a SQL function
-- have been known to cause problems
--
CREATE FUNCTION make_table() RETURNS VOID
AS $$
CREATE TABLE created_table AS SELECT * FROM int8_tbl;
$$ LANGUAGE SQL;
SELECT make_table();
SELECT * FROM created_table;
DROP TABLE created_table;