• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Postgresql源码93Postgresql函数内事务控制实现原理附带Oracle对比

武飞扬头像
高铭杰
帮助1

相关
《Postgresql源码(60)事务系统总结》
《Postgresql源码(93)Postgresql函数内事务控制实现原理(附带Oracle对比)》

0 总结

Postgresql与Oracle都是扁平化处理函数内外的事务控制语句的:即函数内的commit也会直接把函数外面的语句提交掉,函数外面的commit也会把之前函数内部的语句提交掉。

区别是:Postgresql目前还不支持在事务块内,调用带有commit的函数;Oracle是支持的。
(即显示begin启动事务,调用带有commit的函数)

还有游标部分也有一些差别。

1 Postgresql函数内事务控制介绍

PATCH:8561e4840c81f7e345be2df170839846814fa004

增加了plpgsql中事务控制的功能,即实现在plpgsql内执行commit、rollback事务控制语句。

即下列场景

1.1 支持:procedure内的事务控制语句

CREATE TABLE test1 (a int, b text);

CREATE PROCEDURE transaction_test1()
LANGUAGE plpgsql
AS $$
BEGIN
    FOR i IN 0..9 LOOP
        INSERT INTO test1 (a) VALUES (i);
        IF i % 2 = 0 THEN
            COMMIT;
        ELSE
            ROLLBACK;
        END IF;
    END LOOP;
END
$$;

CALL transaction_test1();

SELECT * FROM test1;
 a | b 
--- ---
 0 | 
 2 | 
 4 | 
 6 | 
 8 |
学新通

1.2 支持:匿名块内的事务控制语句

TRUNCATE test1;

DO
LANGUAGE plpgsql
$$
BEGIN
    FOR i IN 0..9 LOOP
        INSERT INTO test1 (a) VALUES (i);
        IF i % 2 = 0 THEN
            COMMIT;
        ELSE
            ROLLBACK;
        END IF;
    END LOOP;
END
$$;

SELECT * FROM test1;
 a | b 
--- ---
 0 | 
 2 | 
 4 | 
 6 | 
 8 |
学新通

1.3 不支持:事务块内调用【带事务控制的procedure】

START TRANSACTION;
CALL transaction_test1();

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

COMMIT;

1.4 不支持:事务块内调用【带事务控制的匿名块】

START TRANSACTION;
DO LANGUAGE plpgsql $$ BEGIN COMMIT; END $$;

ERROR:  invalid transaction termination
CONTEXT:  PL/pgSQL function inline_code_block line 1 at COMMIT

COMMIT;

1.5 不支持:函数内的事务控制语句

CREATE FUNCTION transaction_test2() RETURNS int
LANGUAGE plpgsql
AS $$
BEGIN
    FOR i IN 0..9 LOOP
        INSERT INTO test1 (a) VALUES (i);
        IF i % 2 = 0 THEN
            COMMIT;
        ELSE
            ROLLBACK;
        END IF;
    END LOOP;
    RETURN 1;
END
$$;

SELECT transaction_test2();

ERROR:  invalid transaction termination
CONTEXT:  PL/pgSQL function transaction_test2() line 6 at COMMIT

SELECT * FROM test1;
 a | b 
--- ---

学新通

1.6 不支持:函数内调用【带事务控制的procedure】

CREATE FUNCTION transaction_test3() RETURNS int
LANGUAGE plpgsql
AS $$
BEGIN
    CALL transaction_test1();
    RETURN 1;
END;
$$;
SELECT transaction_test3();
ERROR:  invalid transaction termination
CONTEXT:  PL/pgSQL function transaction_test1() line 6 at COMMIT
SQL statement "CALL transaction_test1()"
PL/pgSQL function transaction_test3() line 3 at SQL statement
SELECT * FROM test1;
 a | b 
--- ---
(0 rows)
学新通

1.7 不支持:函数内调用【带事务控制的匿名块】

CREATE FUNCTION transaction_test4() RETURNS int
LANGUAGE plpgsql
AS $$
BEGIN
    EXECUTE 'DO LANGUAGE plpgsql $x$ BEGIN COMMIT; END $x$';
    RETURN 1;
END;
$$;

SELECT transaction_test4();

ERROR:  invalid transaction termination
CONTEXT:  PL/pgSQL function inline_code_block line 1 at COMMIT
SQL statement "DO LANGUAGE plpgsql $x$ BEGIN COMMIT; END $x$"
PL/pgSQL function transaction_test4() line 3 at EXECUTE

2 Oracle行为分析

2.1 procedure内的rollback会影响外面的SQL吗?:会

测试

set autocommit off;
commit;

drop table test1;
create table test1(a int);

CREATE OR REPLACE PROCEDURE transaction_test2 (x int) AS
BEGIN
    INSERT INTO test1 (a) VALUES (1);
    INSERT INTO test1 (a) VALUES (2);
    rollback;
    INSERT INTO test1 (a) VALUES (3);
    INSERT INTO test1 (a) VALUES (4);
    commit;
    INSERT INTO test1 (a) VALUES (5);
    INSERT INTO test1 (a) VALUES (6);
    rollback;
END;
/


commit;
INSERT INTO test1 (a) VALUES (100);
INSERT INTO test1 (a) VALUES (200);
select * from test1;
call transaction_test2(0);
select * from test1;
commit;
select * from test1;
学新通

结果

SYS@orcl11g>select * from test1;

         A
----------
         3
         4

2.2 procedure内的commit会影响外面的SQL吗?:会

测试

set autocommit off;
commit;

drop table test1;
create table test1(a int);

CREATE OR REPLACE PROCEDURE transaction_test2 (x int) AS
BEGIN
    INSERT INTO test1 (a) VALUES (1);
    INSERT INTO test1 (a) VALUES (2);
    commit;
END;
/


commit;

INSERT INTO test1 (a) VALUES (100);
INSERT INTO test1 (a) VALUES (200);
select * from test1;
call transaction_test2(0);
select * from test1;
rollback;
select * from test1;
学新通

结果

SYS@orcl11g>rollback;

Rollback complete.

SYS@orcl11g>select * from test1;

         A
----------
       100
       200
         1

2.3 procedure外的rollback会影响里面的SQL吗?:会

测试

set autocommit off;
commit;

drop table test1;
create table test1(a int);

CREATE OR REPLACE PROCEDURE transaction_test2 (x int) AS
BEGIN
    INSERT INTO test1 (a) VALUES (1);
    INSERT INTO test1 (a) VALUES (2);
END;
/


commit;

INSERT INTO test1 (a) VALUES (100);
INSERT INTO test1 (a) VALUES (200);
select * from test1;
call transaction_test2(0);
select * from test1;
rollback;
select * from test1;
学新通

结果

SYS@orcl11g>select * from test1;

no rows selected

2.4 procedure外的commit会影响里面的SQL吗?:会

.

2.5 procedure外的savepoint

正常的savepoint是在事务内部使用的,例如:

UPDATE employees 
    SET salary = 7000 
    WHERE last_name = 'Banda';
SAVEPOINT banda_sal;

UPDATE employees 
    SET salary = 12000 
    WHERE last_name = 'Greene';
SAVEPOINT greene_sal;

SELECT SUM(salary) FROM employees;

ROLLBACK TO SAVEPOINT banda_sal;
 
UPDATE employees 
    SET salary = 11000 
    WHERE last_name = 'Greene';
 
COMMIT; 
学新通

如果在事务内调用过程,过程中的事务控制语句会直接结束掉当前事务。

commit;
INSERT INTO test1 (a) VALUES (100);
INSERT INTO test1 (a) VALUES (200);
savepoint sp1;
select * from test1;

call transaction_test2(0);   -- 调用这个函数的效果和执行rollback的效果是完全一样的,都会结束掉当前事务

rollback to savepoint sp1;

ERROR at line 1:
ORA-01086: savepoint 'SP1' never established in this session or is invalid

call 过程导致当前事务结束,检查点自动释放。效果和直接执行commit/rollback相同。

2 Postgresql实现函数内事务控制的源码分析

PATCH在函数调用的入口增加了atomic变量

ExecuteCallStmt(ParseState *pstate, CallStmt *stmt, bool atomic)

atomic含义?

为true表示PL内不能出现事务控制语句,否则报错。即函数、或事务块内的场景为true;过程为false。

atomic如何确定?

atomic的确定受几个函数影响,且function、procedure进入堆栈不同,总结如下:
学新通

具体调用场景中,关键变量的值:

  standard_ProcessUtility函数中context standard_ProcessUtility函数中IsTransactionBlock() plpgsql_call_handler函数中fcinfo->context plpgsql_call_handler函数中CallContext->atomic
【成功】call procedure PROCESS_UTILITY_TOPLEVEL false T_CallContext false
【失败】select function 不走 不走 null null
【失败】事务块内call procedure PROCESS_UTILITY_TOPLEVEL true T_CallContext true
【成功】匿名块内call procedure PROCESS_UTILITY_QUERY_NONATOMIC false T_CallContext false
【失败】事务块内匿名块内call procedure PROCESS_UTILITY_QUERY_NONATOMIC true T_CallContext true
【失败】匿名块内select function 不走 不走 null null

*对于function来说,进入plpgsql_call_handler后fcinfo->context为空,没有后续判断。

3 怎样支持1.3?

注意事务块的状态:

  • 单条SQL的事务状态是TBLOCK_STARTED,CommitTransaction可以直接提交。
  • 事务块的的SQL的事务状态是TBLOCK_INPROGRESS,CommitTransaction不能直接提交。

不能仅仅使用状态机函数CommitTransaction,需要加上commit或rollback的底层处理函数。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgjhgaa
系列文章
更多 icon
同类精品
更多 icon
继续加载