前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Postgresql源码(93)Postgresql函数内事务控制实现原理(附带Oracle对比)

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

作者头像
mingjie
发布2022-12-15 14:58:19
9580
发布2022-12-15 14:58:19
举报

相关 《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内的事务控制语句

代码语言:javascript
复制
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 支持:匿名块内的事务控制语句

代码语言:javascript
复制
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】

代码语言:javascript
复制
START TRANSACTION;
CALL transaction_test1();

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

COMMIT;

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

代码语言:javascript
复制
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 不支持:函数内的事务控制语句

代码语言:javascript
复制
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】

代码语言:javascript
复制
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 不支持:函数内调用【带事务控制的匿名块】

代码语言:javascript
复制
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吗?:会

测试

代码语言:javascript
复制
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;

结果

代码语言:javascript
复制
SYS@orcl11g>select * from test1;

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

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

测试

代码语言:javascript
复制
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;

结果

代码语言:javascript
复制
SYS@orcl11g>rollback;

Rollback complete.

SYS@orcl11g>select * from test1;

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

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

测试

代码语言:javascript
复制
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;

结果

代码语言:javascript
复制
SYS@orcl11g>select * from test1;

no rows selected

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

.

2.5 procedure外的savepoint

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

代码语言:javascript
复制
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; 

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

代码语言:javascript
复制
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变量

代码语言:javascript
复制
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的底层处理函数。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 总结
  • 1 Postgresql函数内事务控制介绍
    • 1.1 支持:procedure内的事务控制语句
      • 1.2 支持:匿名块内的事务控制语句
        • 1.3 不支持:事务块内调用【带事务控制的procedure】
          • 1.4 不支持:事务块内调用【带事务控制的匿名块】
            • 1.5 不支持:函数内的事务控制语句
              • 1.6 不支持:函数内调用【带事务控制的procedure】
                • 1.7 不支持:函数内调用【带事务控制的匿名块】
                • 2 Oracle行为分析
                  • 2.1 procedure内的rollback会影响外面的SQL吗?:会
                    • 2.2 procedure内的commit会影响外面的SQL吗?:会
                      • 2.3 procedure外的rollback会影响里面的SQL吗?:会
                        • 2.4 procedure外的commit会影响里面的SQL吗?:会
                          • 2.5 procedure外的savepoint
                          • 2 Postgresql实现函数内事务控制的源码分析
                            • atomic含义?
                              • atomic如何确定?
                              • 3 怎样支持1.3?
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档