


以 PostgreSQL 中的以下 PL/pgSQL 代码为例。
CREATE OR REPLACE PROCEDURE test_proc ()
AS $$
DECLARE
BEGIN
RAISE NOTICE 'current transaction id: %', txid_current();
END;
$$
LANGUAGE PLPGSQL;
DO $$
BEGIN
RAISE NOTICE 'current transaction id: %', txid_current();
CALL test_proc ();
RAISE NOTICE 'current transaction id: %', txid_current();
END;
$$;
以下是上述匿名块的输出。
NOTICE: current transaction id: 17788
NOTICE: current transaction id: 17788
NOTICE: current transaction id: 17788
现在,让我们看看在 PostgreSQL 中使用 COMMIT 时会发生什么
在 PostgreSQL 的 PL/pgSQL 中,每当一个过程中发生 COMMIT 时,当前事务就会结束,并自动启动一个新事务。
考虑以下带有 COMMIT 的 PL/pgSQL 代码。
CREATE OR REPLACE PROCEDURE test_proc ()
AS $$
DECLARE
BEGIN
RAISE NOTICE 'current transaction id: %', txid_current();
COMMIT;
RAISE NOTICE 'current transaction id: %', txid_current();
END;
$$
LANGUAGE PLPGSQL;
CALL test_proc();
NOTICE: current transaction id: 1183970
NOTICE: current transaction id: 1183971
让我们看看在 Oracle 中使用 COMMIT 时会发生什么
在这种情况下,Oracle 中的 PL/SQL 类似于 PostgreSQL 中的 PL/pgSQL,但是,在创建新事务的方式上仍然存在明显的差异。执行 COMMIT 时,只要遇到第一个 SQL 语句(特别是 DML 命令,如 INSERT 、 UPDATE 、 DELETE 、 SELECT ),就会结束当前事务并开始新事务。
以下以Oracle中的 PL/SQL 代码为例。
CREATE OR REPLACE PROCEDURE test_proc IS
BEGIN
insert into test_ts values(1);
DBMS_OUTPUT.PUT_LINE('transaction id: ' || DBMS_TRANSACTION.LOCAL_TRANSACTION_ID);
COMMIT;
-- No DML performed after the above commit.
DBMS_OUTPUT.PUT_LINE('transaction id: ' || DBMS_TRANSACTION.LOCAL_TRANSACTION_ID);
insert into test_ts values(1);
DBMS_OUTPUT.PUT_LINE('transaction id: ' || DBMS_TRANSACTION.LOCAL_TRANSACTION_ID);
END;
CALL test_proc();
transaction id: 10.4.6777
transaction id:
transaction id: 10.19.6776

现在让我们考虑Oracle与PostgreSQL中的 EXCEPTION 块之间的行为差异。

考虑 PostgreSQL 中的以下 PL/pgSQL 代码。
-- I have created the following procedure
CREATE OR REPLACE PROCEDURE test_proc()
AS $$
DECLARE
BEGIN
insert into test_ts values(1);
RAISE NOTICE 'current transaction id: %', txid_current();
PERFORM 1/0;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'current transaction id: %', txid_current();
END;
$$ LANGUAGE PLPGSQL;
现在使用以下匿名块在PostgreSQL中调用此过程。
DO
$$
BEGIN
insert into test_ts values(1);
RAISE NOTICE 'current transaction id: %', txid_current();
CALL test_proc();
insert into test_ts values(1);
RAISE NOTICE 'current transaction id: %', txid_current();
END;
$$;
上述命令的输出和插入的行数如下所示。
-- Output
NOTICE: current transaction id: 17971
NOTICE: current transaction id: 17971
NOTICE: current transaction id: 17971
NOTICE: current transaction id: 17971
-- Rows Inserted
postgres=# SELECT * from test_ts;
id
----
1
1
(2 rows)
当抛出异常时,它会回滚到保存点。由于这个原因,在 PostgreSQL 的过程 test_proc 中进入异常块之前执行的插入被回滚。所以我们只看到在上面的输出中插入了 2 行。
当一个 EXCEPTION 子句捕捉到错误时,PL/pgSQL 函数的局部变量保持与错误之前相同,但对块内持久数据库状态的所有更改都将回滚。如果我们在 EXCEPTION 块中显式指定 ROLLBACK,则整个事务都会回滚。

考虑以下类似于上述 PL/pgSQL 代码的 PL/SQL 代码。
CREATE OR REPLACE PROCEDURE test_proc IS
dummy number;
BEGIN
insert into test_ts values(1);
DBMS_OUTPUT.PUT_LINE('transaction id: ' || DBMS_TRANSACTION.LOCAL_TRANSACTION_ID);
dummy := 1/0;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('transaction id: ' || DBMS_TRANSACTION.LOCAL_TRANSACTION_ID);
END;
我现在已经执行了以下操作。
BEGIN
insert into test_ts values(1);
DBMS_OUTPUT.PUT_LINE('transaction id: ' || DBMS_TRANSACTION.LOCAL_TRANSACTION_ID);
test_proc();
insert into test_ts values(1);
DBMS_OUTPUT.PUT_LINE('transaction id: ' || DBMS_TRANSACTION.LOCAL_TRANSACTION_ID);
END;
-- Output
transaction id: 8.24.3784
transaction id: 8.24.3784
transaction id: 8.24.3784
transaction id: 8.24.3784
-- Rows Inserted
SQL> select * from test_ts;
ID
----------
1
1
1
在 PL/SQL 中,异常块与事务控制无关,该块在现有事务中运行。当发生异常时,这只是关于分支到另一个代码路径。
与 PL/pgSQL 不同,当 EXCEPTION 子句捕获错误时,块不会回滚。由于这个原因,在异常之前执行的插入在事务中仍然可见,并且可以提交或回滚。

在 PL/SQL 和 PL/pgSQL 中,如果引发错误并且未在其中处理,则调用过程或函数的事务将中止。中止的事务无法提交,如果它们尝试提交,则COMMIT被视为 ROLLBACK。

1、BEGIN 和 END 只是 Oracle 的 PL/SQL 和 PostgreSQL 的 PL/pgSQL 中的句法元素,与交易无关。
2、在PL/pgSQL 的 COMMIT 之后新事务自动启动,而在 PL/SQL 中,新事务仅在遇到第一个 SQL 语句时才开始。
3、PL/pgSQL 中的 EXCEPTION 块在捕获异常时回滚该块直到最后一个保存点,这与 PL/SQL 中的 EXCEPTION 块仅用于代码分支不同。
4、最后,我们还了解到 PL/pgSQL 函数不能有 SAVEPOINT、COMMIT 或 ROLLBACK。
在开始迁移数据库之前,了解 Oracle 和 PostgreSQL 之间的区别很重要。我们之前的文章中已经发布了一些此类差异:处理尾随零、存储过程中的 OUT 参数、移植数组长度等。有关 Oracle 和 PostgreSQL 之间的更多此类差异,请订阅我们的新闻通讯。


文章转载自开源软件联盟PostgreSQL分会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




