bel*_*daz 21 postgresql stored-procedures isolation-level
希望这是一个简单的问题,但我还没有找到一个像样的答案.我可靠地告知PostgreSQL(特别是版本9.0.4)中的存储过程(用户定义的DB函数)本质上是事务性的,因为它们是通过SELECT语句调用的,而SELECT语句本身就是一个事务.那么如何选择存储过程的隔离级别呢?我相信在其他DBMS中,所需的事务块将被包装在START TRANSACTION块中,其中所需的隔离级别是可选参数.
作为一个具体的例子,说我想这样做:
CREATE FUNCTION add_new_row(rowtext TEXT)
RETURNS VOID AS
$$
BEGIN
INSERT INTO data_table VALUES (rowtext);
UPDATE row_counts_table SET count=count+1;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
Run Code Online (Sandbox Code Playgroud)
并且想象一下,我想确保此函数始终作为可序列化事务执行(是的,是的,PostgreSQL SERIALIZABLE不是可正确序列化的,但这不是重点).我不想要求它被称为
START TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT add_new_row('foo');
COMMIT;
Run Code Online (Sandbox Code Playgroud)
那么如何将所需的隔离级别下移到函数中呢?我相信我不能像BEGIN报告手册那样将隔离级别放在声明中
重要的是不要混淆使用BEGIN/END将PL/pgSQL中的语句分组与用于事务控制的类似命名的SQL命令.PL/pgSQL的BEGIN/END仅用于分组; 他们不会开始或结束交易.函数和触发器过程总是在一个由外层查询建立起来的事务中执行 - 他们不能开始或者提交该事务,因为不会有什么背景让他们在执行.
对我来说最明显的方法是SET TRANSACTION在函数定义中的某处使用,例如:
CREATE FUNCTION add_new_row(rowtext TEXT)
RETURNS VOID AS
$$
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO data_table VALUES (rowtext);
UPDATE row_counts_table SET count=count+1;
END;
$$
LANGUAGE plpgsql
SECURITY DEFINER;
Run Code Online (Sandbox Code Playgroud)
虽然这是可以接受的,但我不能完全依赖它来工作.该文档的SET TRANSACTION说
如果在没有事先START TRANSACTION或BEGIN的情况下执行SET TRANSACTION,则它似乎无效,因为事务将立即结束.
这让我感到困惑,因为如果我调用一个单独的SELECT add_new_row('foo');语句,我会期望(假设我没有禁用自动提交)SELECT作为具有会话默认隔离级别的单行事务运行.
该手册还写道:
在执行事务的第一个查询或数据修改语句(SELECT,INSERT,DELETE,UPDATE,FETCH或COPY)后,不能更改事务隔离级别.
那么如果从具有较低隔离级别的事务中调用函数会发生什么,例如:
START TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPDATE row_counts_table SET count=0;
SELECT add_new_row('foo');
COMMIT;
Run Code Online (Sandbox Code Playgroud)
对于一个额外的问题:函数的语言有什么不同吗?是否可以在PL/pgSQL中设置隔离级别与普通SQL不同?
我是标准的粉丝,并记录了最佳实践,所以任何体面的参考将不胜感激.
Pet*_*aut 18
你不能这样做.
您可以做的是让您的函数检查当前事务隔离级别是什么,如果不是您想要的那个,则中止.您可以通过运行SELECT current_setting('transaction_isolation')然后检查结果来执行此操作.