希望是一个简单的问题,但我还没有找到合适的答案。我被可靠地告知 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;
想象一下,我想确保这个函数总是作为一个可序列化的事务执行(是的,是的,PostgreSQL SERIALIZABLE 不是正确的可序列化的,但这不是重点)。我不想要求它被称为
START TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT add_new_row('foo');
COMMIT;
那么如何将所需的隔离级别下推到函数中呢?我相信我不能只把隔离级别放在BEGIN
声明中,就像手册说的那样
重要的是不要将在 PL/pgSQL 中用于分组语句的 BEGIN/END 与用于事务控制的类似名称的 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;
虽然这会被接受,但尚不清楚我是否可以依靠它来工作。文档SET TRANSACTION
说_ _
如果 SET TRANSACTION 在没有事先 START TRANSACTION 或 BEGIN 的情况下执行,它似乎没有任何效果,因为事务将立即结束。
这让我感到困惑,因为如果我调用一个单独的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;
对于一个额外的问题:函数的语言有什么不同吗?在 PL/pgSQL 中设置隔离级别是否与在普通 SQL 中不同?
我是标准和记录最佳实践的粉丝,因此任何体面的参考资料将不胜感激。