当前位置:网站首页>Procedure in PostgreSQL supports transaction syntax (instance & Analysis)

Procedure in PostgreSQL supports transaction syntax (instance & Analysis)

2022-07-07 07:12:00 mingjie73

relevant

《Postgresql Source code (60) Transaction system summary 》

https://www.postgresql.org/docs/current/plpgsql-transactions.html

example 1:PROCEDURE Submission can be used internally 、 Rollback statement

drop table test1;
create table test1 (a int);
CREATE or replace PROCEDURE transaction_test1()
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO test1 (a) VALUES (2);
    COMMIT;
    INSERT INTO test1 (a) VALUES (3);
    ROLLBACK;
END;
$$;

CALL transaction_test1();
select * from test1;
 a 
---
 2

commit What does the statement do ?

Execute the upper level transaction state flow function :

  1. perform CommitTransactionCommand
  2. perform StartTransactionCommand
static int
exec_stmt_commit(PLpgSQL_execstate *estate, PLpgSQL_stmt_commit *stmt)
{
    
	if (stmt->chain)
		SPI_commit_and_chain();
	else
	{
    
		SPI_commit();
		SPI_start_transaction();
	}
  ...
	return PLPGSQL_RC_OK;
}

rollback What does the statement do ?

Execute the upper level transaction state flow function :

  1. perform AbortCurrentTransaction
  2. perform StartTransactionCommand
static int
exec_stmt_rollback(PLpgSQL_execstate *estate, PLpgSQL_stmt_rollback *stmt)
{
    
	if (stmt->chain)
		SPI_rollback_and_chain();
	else
	{
    
		SPI_rollback();
		SPI_start_transaction();
	}
  ...
	return PLPGSQL_RC_OK;
}

example 2:PROCEDURE If an internal error is reported, the executed statement will be rolled back automatically

drop table test1;
create table test1 (a int);
CREATE or replace PROCEDURE transaction_test1()
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO test1 (a) VALUES (2);
    INSERT INTO test1 (a) VALUES (3);
    RAISE division_by_zero;
END;
$$;

CALL transaction_test1();
ERROR:  division_by_zero
CONTEXT:  PL/pgSQL function transaction_test1() line 5 at RAISE

select * from test1;
 a 
---
(0 rows)

How transactions are rolled back ?

//  Trigger ereport ERROR
RAISE division_by_zero;  

// jump  To :
PostgresMain
  if (sigsetjmp(local_sigjmp_buf, 1) != 0)
    AbortCurrentTransaction()

go AbortCurrentTransaction Trigger rollback action

example 3:PROCEDURE Internal error reporting will not roll over the submitted statements

drop table test1;
create table test1 (a int);
CREATE or replace PROCEDURE transaction_test1()
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO test1 (a) VALUES (2);
    COMMIT;
    INSERT INTO test1 (a) VALUES (3);
    RAISE division_by_zero;
END;
$$;

CALL transaction_test1();
select * from test1;
 a 
---
 2
(1 row)

Reference examples 1 Analysis results of ,commit After the execution, a new transaction will start , The subsequent saving does not affect the previously committed transactions .

example 4:PROCEDURE contain EXCEPTION The statement block of does not support COMMIT

drop table test1;
create table test1 (a int);
CREATE or replace PROCEDURE transaction_test1()
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO test1 (a) VALUES (2);
    COMMIT;
    INSERT INTO test1 (a) VALUES (3);
    RAISE division_by_zero;
EXCEPTION
    WHEN division_by_zero THEN
        RAISE NOTICE 'caught division_by_zero';
END;
$$;

CALL transaction_test1();
ERROR:  cannot commit while a subtransaction is active
CONTEXT:  PL/pgSQL function transaction_test1() line 4 at COMMIT

select * from test1;
 a 
---
(0 rows)

If you go EXCEPTION Statement block words , Will take the whole block Wrapped in a sub transaction , Execution is not supported in sub transactions commit.

exec_stmt_block
  ...
  if (block->exceptions)
    //  Started a sub transaction 
    BeginInternalSubTransaction
    PG_TRY()
      exec_stmts
    PG_CATCH()
      //  If there is any abnormality , End the whole sub transaction 
      RollbackAndReleaseCurrentSubTransaction
    

example 5:function It is atomic and does not support partial submission

drop table test1;
create table test1 (a int);
CREATE or replace function transaction_test1()
returns void
LANGUAGE plpgsql
AS $$
BEGIN
    INSERT INTO test1 (a) VALUES (2);
    COMMIT;
    INSERT INTO test1 (a) VALUES (3);
    ROLLBACK;
END;
$$;

select transaction_test1();
ERROR:  invalid transaction termination
CONTEXT:  PL/pgSQL function transaction_test1() line 4 at COMMIT

reason :

Before executing the function , initialization SPI System

If the incoming fcinfo->context It's a call context Just configure nonatomic

plpgsql_call_handler
  nonatomic = fcinfo->context 
                && IsA(fcinfo->context, CallContext) 
                && !castNode(CallContext, fcinfo->context)->atomic;
  SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0))
    _SPI_current->atomic = (options & SPI_OPT_NONATOMIC ? false : true);

If it is call procedure sentence

_SPI_current->atomic = false;

So in the implementation exec_stmt_commit when , No mistake. .

exec_stmt_commit
  SPI_commit
    _SPI_commit
      	if (_SPI_current->atomic)
		      ereport(ERROR,
				     (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
				     errmsg("invalid transaction termination")));

If it is function It will report an error and quit .

原网站

版权声明
本文为[mingjie73]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207070205187376.html