postgresql - 为 postgresql 存储过程设置隔离级别

标签 postgresql stored-procedures isolation-level

希望这是一个简单的问题,但我还没有找到合适的答案。我可靠地得知 PostgreSQL(特别是版本 9.0.4)中的存储过程(用户定义的 DB 函数)本质上是事务性的,因为它们是通过 SELECT 语句调用的,而 SELECT 语句本身就是一个事务。那么如何选择存储过程的隔离级别呢?我相信在其他 DBMS 中,所需的事务 block 将包装在 START TRANSACTION block 中,所需的隔离级别是可选参数。

作为一个具体的虚构示例,假设我想这样做:

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 语句中,如 the manual says

It is important not to confuse the use of BEGIN/END for grouping statements in PL/pgSQL with the similarly-named SQL commands for transaction control. PL/pgSQL's BEGIN/END are only for grouping; they do not start or end a transaction. Functions and trigger procedures are always executed within a transaction established by an outer query — they cannot start or commit that transaction, since there would be no context for them to execute in.

对我来说最明显的方法是在函数定义的某处使用 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;

虽然这会被接受,但我还不清楚我能否依靠它来工作。 documentation对于 SET TRANSACTION

If SET TRANSACTION is executed without a prior START TRANSACTION or BEGIN, it will appear to have no effect, since the transaction will immediately end.

这让我感到困惑,因为如果我调用一个单独的 SELECT add_new_row('foo'); 语句,我会期望(假设我没有禁用自动提交)SELECT 作为一个单一的运行- 具有 session 默认隔离级别的行事务。

manual还说:

The transaction isolation level cannot be changed after the first query or data-modification statement (SELECT, INSERT, DELETE, UPDATE, FETCH, or COPY) of a transaction has been executed.

那么如果从具有较低隔离级别的事务中调用该函数会发生什么,例如:

START TRANSACTION ISOLATION LEVEL READ COMMITTED;
UPDATE row_counts_table SET count=0;
SELECT add_new_row('foo');
COMMIT;

额外的问题:函数的语言有什么不同吗?在 PL/pgSQL 中设置的隔离级别是否与在普通 SQL 中设置的不同?

我是标准和记录的最佳实践的粉丝,所以任何体面的引用都会受到赞赏。

最佳答案

你不能那样做。

您可以做的是让您的函数检查当前事务隔离级别是什么,如果不是您想要的级别,则中止。您可以通过运行 SELECT current_setting('transaction_isolation') 然后检查结果来执行此操作。

关于postgresql - 为 postgresql 存储过程设置隔离级别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6274457/

相关文章:

ruby-on-rails-3 - PgHero Rails - 列 "pid"不存在

MySQL 过程无法识别参数

mysql - 更新后从另一个事务可见,是MySQL MVCC的bug吗?

postgresql - 如何连接 Jboss-as-7.1.1 和 Postgresql

SQL删除具有不同值的重复行,但保留并在单行中显示它们

postgresql - 将二进制内容附加到 PostgreSQL 数据库中的 bytea 列

java - jsp jSTL sql 与 mysql 中一样的奇怪行为

mysql 错误 (#1064) 存储过程创建错误

tomcat - 我可以从 Tomcat 上下文设置 JDBC 隔离级别吗?

postgresql - 错误 : duplicate key value violates unique constraint "xak1fact_dim_relationship"