aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/spi.c7
-rw-r--r--src/pl/plpgsql/src/expected/plpgsql_transaction.out19
-rw-r--r--src/pl/plpgsql/src/pl_exec.c32
-rw-r--r--src/pl/plpgsql/src/sql/plpgsql_transaction.sql20
4 files changed, 72 insertions, 6 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 22dd55c3783..5756365c8f3 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -228,8 +228,13 @@ SPI_commit(void)
_SPI_current->internal_xact = true;
- if (ActiveSnapshotSet())
+ /*
+ * Before committing, pop all active snapshots to avoid error about
+ * "snapshot %p still active".
+ */
+ while (ActiveSnapshotSet())
PopActiveSnapshot();
+
CommitTransactionCommand();
MemoryContextSwitchTo(oldcontext);
diff --git a/src/pl/plpgsql/src/expected/plpgsql_transaction.out b/src/pl/plpgsql/src/expected/plpgsql_transaction.out
index 7f008ac57e9..274b2c6f170 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_transaction.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_transaction.out
@@ -432,6 +432,25 @@ END;
$$;
ERROR: EXECUTE of transaction commands is not implemented
CONTEXT: PL/pgSQL function inline_code_block line 3 at EXECUTE
+-- snapshot handling test
+TRUNCATE test2;
+CREATE PROCEDURE transaction_test9()
+LANGUAGE SQL
+AS $$
+INSERT INTO test2 VALUES (42);
+$$;
+DO LANGUAGE plpgsql $$
+BEGIN
+ ROLLBACK;
+ CALL transaction_test9();
+END
+$$;
+SELECT * FROM test2;
+ x
+----
+ 42
+(1 row)
+
DROP TABLE test1;
DROP TABLE test2;
DROP TABLE test3;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 66ecf5eb559..e39f7357bd5 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -2075,6 +2075,7 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
ParamListInfo paramLI;
LocalTransactionId before_lxid;
LocalTransactionId after_lxid;
+ bool pushed_active_snap = false;
int rc;
if (expr->plan == NULL)
@@ -2090,6 +2091,7 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
/*
* The procedure call could end transactions, which would upset the
* snapshot management in SPI_execute*, so don't let it do it.
+ * Instead, we set the snapshots ourselves below.
*/
expr->plan->no_snapshots = true;
}
@@ -2098,6 +2100,16 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
before_lxid = MyProc->lxid;
+ /*
+ * Set snapshot only for non-read-only procedures, similar to SPI
+ * behavior.
+ */
+ if (!estate->readonly_func)
+ {
+ PushActiveSnapshot(GetTransactionSnapshot());
+ pushed_active_snap = true;
+ }
+
PG_TRY();
{
rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
@@ -2126,12 +2138,22 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
elog(ERROR, "SPI_execute_plan_with_paramlist failed executing query \"%s\": %s",
expr->query, SPI_result_code_string(rc));
- /*
- * If we are in a new transaction after the call, we need to reset some
- * internal state.
- */
- if (before_lxid != after_lxid)
+ if (before_lxid == after_lxid)
+ {
+ /*
+ * If we are still in the same transaction after the call, pop the
+ * snapshot that we might have pushed. (If it's a new transaction,
+ * then all the snapshots are gone already.)
+ */
+ if (pushed_active_snap)
+ PopActiveSnapshot();
+ }
+ else
{
+ /*
+ * If we are in a new transaction after the call, we need to reset
+ * some internal state.
+ */
estate->simple_eval_estate = NULL;
plpgsql_create_econtext(estate);
}
diff --git a/src/pl/plpgsql/src/sql/plpgsql_transaction.sql b/src/pl/plpgsql/src/sql/plpgsql_transaction.sql
index eddc518bb6a..1624aed6eca 100644
--- a/src/pl/plpgsql/src/sql/plpgsql_transaction.sql
+++ b/src/pl/plpgsql/src/sql/plpgsql_transaction.sql
@@ -354,6 +354,26 @@ BEGIN
END;
$$;
+
+-- snapshot handling test
+TRUNCATE test2;
+
+CREATE PROCEDURE transaction_test9()
+LANGUAGE SQL
+AS $$
+INSERT INTO test2 VALUES (42);
+$$;
+
+DO LANGUAGE plpgsql $$
+BEGIN
+ ROLLBACK;
+ CALL transaction_test9();
+END
+$$;
+
+SELECT * FROM test2;
+
+
DROP TABLE test1;
DROP TABLE test2;
DROP TABLE test3;