Skip to content

Commit 7efa841

Browse files
committed
Rethink plpgsql's way of handling SPI execution during an exception block.
We don't really want to start a new SPI connection, just keep using the old one; otherwise we have memory management problems as illustrated by John Kennedy's bug report of today. This requires a bit of a hack to ensure the SPI stack state is properly restored, but then again what we were doing before was a hack too, strictly speaking. Add a regression test to cover this case.
1 parent 2bb3bcf commit 7efa841

File tree

5 files changed

+72
-20
lines changed

5 files changed

+72
-20
lines changed

src/backend/executor/spi.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.131 2004/10/13 01:25:10 neilc Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.132 2004/11/16 18:10:13 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -270,6 +270,14 @@ SPI_pop(void)
270270
_SPI_curid--;
271271
}
272272

273+
/* Restore state of SPI stack after aborting a subtransaction */
274+
void
275+
SPI_restore_connection(void)
276+
{
277+
Assert(_SPI_connected >= 0);
278+
_SPI_curid = _SPI_connected - 1;
279+
}
280+
273281
/* Parse, plan, and execute a querystring */
274282
int
275283
SPI_execute(const char *src, bool read_only, int tcount)

src/include/executor/spi.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* spi.h
44
*
5-
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.49 2004/09/16 16:58:40 tgl Exp $
5+
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.50 2004/11/16 18:10:13 tgl Exp $
66
*
77
*-------------------------------------------------------------------------
88
*/
@@ -81,6 +81,7 @@ extern int SPI_connect(void);
8181
extern int SPI_finish(void);
8282
extern void SPI_push(void);
8383
extern void SPI_pop(void);
84+
extern void SPI_restore_connection(void);
8485
extern int SPI_execute(const char *src, bool read_only, int tcount);
8586
extern int SPI_execute_plan(void *plan, Datum *Values, const char *Nulls,
8687
bool read_only, int tcount);

src/pl/plpgsql/src/pl_exec.c

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.120 2004/09/16 16:58:44 tgl Exp $
6+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.121 2004/11/16 18:10:14 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -899,20 +899,11 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
899899
MemoryContext oldcontext = CurrentMemoryContext;
900900
ResourceOwner oldowner = CurrentResourceOwner;
901901
volatile bool caught = false;
902-
int xrc;
903902

904-
/*
905-
* Start a subtransaction, and re-connect to SPI within it
906-
*/
907-
SPI_push();
908903
BeginInternalSubTransaction(NULL);
909904
/* Want to run statements inside function's memory context */
910905
MemoryContextSwitchTo(oldcontext);
911906

912-
if ((xrc = SPI_connect()) != SPI_OK_CONNECT)
913-
elog(ERROR, "SPI_connect failed: %s",
914-
SPI_result_code_string(xrc));
915-
916907
PG_TRY();
917908
{
918909
rc = exec_stmts(estate, block->body);
@@ -928,12 +919,17 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
928919
edata = CopyErrorData();
929920
FlushErrorState();
930921

931-
/* Abort the inner transaction (and inner SPI connection) */
922+
/* Abort the inner transaction */
932923
RollbackAndReleaseCurrentSubTransaction();
933924
MemoryContextSwitchTo(oldcontext);
934925
CurrentResourceOwner = oldowner;
935926

936-
SPI_pop();
927+
/*
928+
* If AtEOSubXact_SPI() popped any SPI context of the subxact,
929+
* it will have left us in a disconnected state. We need this
930+
* hack to return to connected state.
931+
*/
932+
SPI_restore_connection();
937933

938934
/* Look for a matching exception handler */
939935
exceptions = block->exceptions;
@@ -960,15 +956,14 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
960956
/* Commit the inner transaction, return to outer xact context */
961957
if (!caught)
962958
{
963-
if ((xrc = SPI_finish()) != SPI_OK_FINISH)
964-
elog(ERROR, "SPI_finish failed: %s",
965-
SPI_result_code_string(xrc));
966-
967959
ReleaseCurrentSubTransaction();
968960
MemoryContextSwitchTo(oldcontext);
969961
CurrentResourceOwner = oldowner;
970-
971-
SPI_pop();
962+
/*
963+
* AtEOSubXact_SPI() should not have popped any SPI context,
964+
* but just in case it did, make sure we remain connected.
965+
*/
966+
SPI_restore_connection();
972967
}
973968
}
974969
else

src/test/regress/expected/plpgsql.out

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,6 +1931,36 @@ select * from foo;
19311931
20
19321932
(2 rows)
19331933

1934+
-- Test for pass-by-ref values being stored in proper context
1935+
create function test_variable_storage() returns text as $$
1936+
declare x text;
1937+
begin
1938+
x := '1234';
1939+
begin
1940+
x := x || '5678';
1941+
-- force error inside subtransaction SPI context
1942+
perform trap_zero_divide(-100);
1943+
exception
1944+
when others then
1945+
x := x || '9012';
1946+
end;
1947+
return x;
1948+
end$$ language plpgsql;
1949+
select test_variable_storage();
1950+
NOTICE: should see this
1951+
CONTEXT: SQL statement "SELECT trap_zero_divide(-100)"
1952+
PL/pgSQL function "test_variable_storage" line 7 at perform
1953+
NOTICE: should see this only if -100 <> 0
1954+
CONTEXT: SQL statement "SELECT trap_zero_divide(-100)"
1955+
PL/pgSQL function "test_variable_storage" line 7 at perform
1956+
NOTICE: should see this only if -100 fits in smallint
1957+
CONTEXT: SQL statement "SELECT trap_zero_divide(-100)"
1958+
PL/pgSQL function "test_variable_storage" line 7 at perform
1959+
test_variable_storage
1960+
-----------------------
1961+
123456789012
1962+
(1 row)
1963+
19341964
--
19351965
-- test foreign key error trapping
19361966
--

src/test/regress/sql/plpgsql.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,24 @@ reset statement_timeout;
16961696

16971697
select * from foo;
16981698

1699+
-- Test for pass-by-ref values being stored in proper context
1700+
create function test_variable_storage() returns text as $$
1701+
declare x text;
1702+
begin
1703+
x := '1234';
1704+
begin
1705+
x := x || '5678';
1706+
-- force error inside subtransaction SPI context
1707+
perform trap_zero_divide(-100);
1708+
exception
1709+
when others then
1710+
x := x || '9012';
1711+
end;
1712+
return x;
1713+
end$$ language plpgsql;
1714+
1715+
select test_variable_storage();
1716+
16991717
--
17001718
-- test foreign key error trapping
17011719
--

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy