]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Rethink plpgsql's way of handling SPI execution during an exception block.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 Nov 2004 18:10:16 +0000 (18:10 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 Nov 2004 18:10:16 +0000 (18:10 +0000)
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.

src/backend/executor/spi.c
src/include/executor/spi.h
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/plpgsql.out
src/test/regress/sql/plpgsql.sql

index b533470e1d523b1a87c45720ba9e54a7d7b25b07..4549ba00080d3b95105357fe90760f7a8e242c91 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.131 2004/10/13 01:25:10 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.132 2004/11/16 18:10:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -270,6 +270,14 @@ SPI_pop(void)
        _SPI_curid--;
 }
 
+/* Restore state of SPI stack after aborting a subtransaction */
+void
+SPI_restore_connection(void)
+{
+       Assert(_SPI_connected >= 0);
+       _SPI_curid = _SPI_connected - 1;
+}
+
 /* Parse, plan, and execute a querystring */
 int
 SPI_execute(const char *src, bool read_only, int tcount)
index 3014dbce84083040c2989f35ef61191ee2ae1214..d5ba89fa3eaacc0cd03a3ad0876409f0aebf47ff 100644 (file)
@@ -2,7 +2,7 @@
  *
  * spi.h
  *
- * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.49 2004/09/16 16:58:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.50 2004/11/16 18:10:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -81,6 +81,7 @@ extern int    SPI_connect(void);
 extern int     SPI_finish(void);
 extern void SPI_push(void);
 extern void SPI_pop(void);
+extern void SPI_restore_connection(void);
 extern int     SPI_execute(const char *src, bool read_only, int tcount);
 extern int     SPI_execute_plan(void *plan, Datum *Values, const char *Nulls,
                                                         bool read_only, int tcount);
index 12ca6c0888ab8aa78629d0d22882e20f1d63f2a4..fddbfefac475b549b6b0ca922a83eccc975e527d 100644 (file)
@@ -3,7 +3,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.120 2004/09/16 16:58:44 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.121 2004/11/16 18:10:14 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -899,20 +899,11 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
                MemoryContext oldcontext = CurrentMemoryContext;
                ResourceOwner oldowner = CurrentResourceOwner;
                volatile bool caught = false;
-               int                     xrc;
 
-               /*
-                * Start a subtransaction, and re-connect to SPI within it
-                */
-               SPI_push();
                BeginInternalSubTransaction(NULL);
                /* Want to run statements inside function's memory context */
                MemoryContextSwitchTo(oldcontext);
 
-               if ((xrc = SPI_connect()) != SPI_OK_CONNECT)
-                       elog(ERROR, "SPI_connect failed: %s",
-                                SPI_result_code_string(xrc));
-
                PG_TRY();
                {
                        rc = exec_stmts(estate, block->body);
@@ -928,12 +919,17 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
                        edata = CopyErrorData();
                        FlushErrorState();
 
-                       /* Abort the inner transaction (and inner SPI connection) */
+                       /* Abort the inner transaction */
                        RollbackAndReleaseCurrentSubTransaction();
                        MemoryContextSwitchTo(oldcontext);
                        CurrentResourceOwner = oldowner;
 
-                       SPI_pop();
+                       /*
+                        * If AtEOSubXact_SPI() popped any SPI context of the subxact,
+                        * it will have left us in a disconnected state.  We need this
+                        * hack to return to connected state.
+                        */
+                       SPI_restore_connection();
 
                        /* Look for a matching exception handler */
                        exceptions = block->exceptions;
@@ -960,15 +956,14 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
                /* Commit the inner transaction, return to outer xact context */
                if (!caught)
                {
-                       if ((xrc = SPI_finish()) != SPI_OK_FINISH)
-                               elog(ERROR, "SPI_finish failed: %s",
-                                        SPI_result_code_string(xrc));
-
                        ReleaseCurrentSubTransaction();
                        MemoryContextSwitchTo(oldcontext);
                        CurrentResourceOwner = oldowner;
-
-                       SPI_pop();
+                       /*
+                        * AtEOSubXact_SPI() should not have popped any SPI context,
+                        * but just in case it did, make sure we remain connected.
+                        */
+                       SPI_restore_connection();
                }
        }
        else
index e1b57f9716b4719554e29309508642846ff1b1d7..8674fa9531d4423e31d3d39dd8f0c12da8706650 100644 (file)
@@ -1931,6 +1931,36 @@ select * from foo;
  20
 (2 rows)
 
+-- Test for pass-by-ref values being stored in proper context
+create function test_variable_storage() returns text as $$
+declare x text;
+begin
+  x := '1234';
+  begin
+    x := x || '5678';
+    -- force error inside subtransaction SPI context
+    perform trap_zero_divide(-100);
+  exception
+    when others then
+      x := x || '9012';
+  end;
+  return x;
+end$$ language plpgsql;
+select test_variable_storage();
+NOTICE:  should see this
+CONTEXT:  SQL statement "SELECT  trap_zero_divide(-100)"
+PL/pgSQL function "test_variable_storage" line 7 at perform
+NOTICE:  should see this only if -100 <> 0
+CONTEXT:  SQL statement "SELECT  trap_zero_divide(-100)"
+PL/pgSQL function "test_variable_storage" line 7 at perform
+NOTICE:  should see this only if -100 fits in smallint
+CONTEXT:  SQL statement "SELECT  trap_zero_divide(-100)"
+PL/pgSQL function "test_variable_storage" line 7 at perform
+ test_variable_storage 
+-----------------------
+ 123456789012
+(1 row)
+
 --
 -- test foreign key error trapping
 --
index 367a73986e1e20251c1db72a6dc9c041fb756243..f9f307b1ac041b2a6101175c41373c0f76368fa3 100644 (file)
@@ -1696,6 +1696,24 @@ reset statement_timeout;
 
 select * from foo;
 
+-- Test for pass-by-ref values being stored in proper context
+create function test_variable_storage() returns text as $$
+declare x text;
+begin
+  x := '1234';
+  begin
+    x := x || '5678';
+    -- force error inside subtransaction SPI context
+    perform trap_zero_divide(-100);
+  exception
+    when others then
+      x := x || '9012';
+  end;
+  return x;
+end$$ language plpgsql;
+
+select test_variable_storage();
+
 --
 -- test foreign key error trapping
 --