mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-23 20:37:19 +02:00
Fix assert in nested SQL procedure call
When executing CALL in PL/pgSQL, we need to set a snapshot before invoking the to-be-called procedure. Otherwise, the to-be-called procedure might end up running without a snapshot. For LANGUAGE SQL procedures, this would result in an assertion failure. (For most other languages, this is usually not a problem, because those use SPI and SPI sets snapshots in most cases.) Setting the snapshot restores the behavior of how CALL worked when it was handled as a generic SQL statement in PL/pgSQL (exec_stmt_execsql()). This change revealed another problem: In SPI_commit(), we popped the active snapshot before committing the transaction, to avoid "snapshot %p still active" errors. However, there is no particular reason why only at most one snapshot should be on the stack. So change this to pop all active snapshots instead of only one.
This commit is contained in:
parent
6f50b1c70f
commit
d89348976c
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user