在已打开的事务中是否仍然可以使用已删除(或更改)的功能?

Wil*_*ard 7 postgresql locked-objects transaction functions

我发现

但是没有答案并且与我的问题不完全相同(尽管非常相似)。


假设我执行以下操作:

  1. 创建函数 myfunc()
  2. 从客户端 A 开始事务
  3. 从客户端 B 开始事务
  4. 在事务B中,使用“创建或替换功能”修改定义 myfunc()
  5. 提交事务 B
  6. myfunc()从事务 A调用

第 6 步会发生什么? 我是否调用了步骤 1 中定义的原始函数?还是第 4 步中的修改形式(在第 5 步中提交)?


如果函数在第 4 步中被删除而不是被修改,那么第 6 步会失败还是成功?(这可能是同一个问题,但修改的工作方式可能不同。)


关于这个的文档在哪里?

ype*_*eᵀᴹ 6

有趣的问题。

从一个小测试来看,函数修改和删除似乎是事务性的。这意味着 - 在任何隔离级别 - 当事务 2 修改或删除该函数时,事务 1 不会注意到它并仍然使用该函数的旧版本。

对函数的任何更改仅在事务提交后才可见,并且仅对在该提交之后启动的事务可见。隔离级别无关紧要,因为测试的函数没有从任何表中读取任何数据。

-- Create Function
x=# create or replace function f() returns integer as
$$ select 1 ; $$ immutable language sql ;
CREATE FUNCTION

-- TRAN 1
x=# begin ;
BEGIN
x=# select * from f() ;
 f 
---
 1
(1 row)
                    -- TRAN 2
                    x=# begin ;
                    BEGIN
                    x=# drop function f () ;
                    DROP FUNCTION
                    x=# commit ;
                    COMMIT
-- TRAN 1
x=# select * from f() ;
 f 
---
 1
(1 row)
x=# commit ;
COMMIT

-- After COMMIT
x=# select * from f() ;
ERROR:  function f() does not exist
LINE 1: select * from f() ;
                      ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
x=# 
Run Code Online (Sandbox Code Playgroud)

在稍微不同的情况下,如果两个事务都尝试修改函数,那么只有一个成功,另一个被阻塞,如果第一个提交则失败。

  • 我不同意结论。如果在读提交模式下从 TRAN1 中删除第一个 `select * from f()`,则第二个 `select * from f()` 失败。上面的测试似乎展示了 TRAN1 本地查找缓存的效果,而不是您在答案中得出的结论。 (2认同)

Erw*_*ter 5

第 6 步会发生什么?

事务 A 立即看到函数的更新定义myfunc()。(但是请看下面缓存的效果。)

如果函数在步骤 4 中被删除而不是被修改,那么步骤 6 会失败还是成功?

它会失败。(但是请看下面缓存的效果。)

Postgres DDL 命令是完全事务性的。当事务 B 未提交时,两个事务将继续看到该函数的不同版本。但并发事务确实看到系统目录中已提交的更改。在默认隔离级别下似乎很明显READ COMMITTED。但您甚至无法通过隔离级别REPEATABLE READ或来防止这种情况发生SERIALIZABLE

如果您应该在事务 B 提交更改之前调用事务 A 中的函数,则本地缓存可能会干扰。在我的测试中,在下一个调用意识到更改并做出相应响应之前,又一个调用使用了缓存(旧)函数。

我没有找到系统目录缓存如何准确执行此操作的文档(仍然可能存在于某处)。我不相信最后一位(从缓存应答的另一个调用)是最好的行为。


顺便说一句,您的步骤 3. - 5. 可以减少到只有 4.,没有任何区别。显式或隐式事务包装器的工作原理相同:

3. 从客户端 B 启动一个事务
4. 在事务 B 中,使用“创建或替换函数”修改 myfunc() 的定义
5. 提交事务 B