Изоляция и системный каталог

Создаем функцию, возвращающую единицу:

=> CREATE DATABASE mvcc_isolation;
CREATE DATABASE
=> \c mvcc_isolation
You are now connected to database "mvcc_isolation" as user "postgres".
=> CREATE FUNCTION f() RETURNS integer AS $$
  SELECT 1;
$$ LANGUAGE sql;
CREATE FUNCTION

Начинаем транзакцию Read Committed во втором сеансе:

=> \c mvcc_isolation
You are now connected to database "mvcc_isolation" as user "postgres".
=> BEGIN ISOLATION LEVEL READ COMMITTED;
BEGIN
=> SELECT f();
 f 
---
 1
(1 row)

Изменяем функцию:

=> CREATE OR REPLACE FUNCTION f() RETURNS integer AS $$
  SELECT 2;
$$ LANGUAGE sql;
CREATE FUNCTION
=> SELECT f();
 f 
---
 2
(1 row)

=> SELECT f();
 f 
---
 1
(1 row)

=> COMMIT;
COMMIT

Многоверсионность в PostgreSQL распространяется и на таблицы системного каталога. В том числе и на pg_proc, где хранится исходный код функций. Поэтому разные транзакции могут видеть и вызывать разные экземпляры одной и той же функции.

Но при этом даже на уровне Read Committed не возникает неповторяемого чтения - этим таблицы системного каталога отличаются от обычных.

Момент времени, на который видны данные

Пустая таблица:

=> CREATE TABLE t(id integer);
CREATE TABLE

Открываем транзакцию Repeatable Read:

=> BEGIN ISOLATION LEVEL REPEATABLE READ;
BEGIN

Добавляем строку в таблицу (изменения фиксируются):

=> INSERT INTO t VALUES (1);
INSERT 0 1

Проверяем содержимое таблицы в открытой транзакции:

=> SELECT * FROM t;
 id 
----
  1
(1 row)

Строка (неожиданно) видна. Дело в том, что транзакции Repeatable Read (и Serializable) видят данные на момент начала первой команды, а не на момент начала транзакции.

Если сейчас добавить в таблицу новые строки, результат повторной выборки уже не изменится.

=> INSERT INTO t VALUES (2);
INSERT 0 1
=> SELECT * FROM t;
 id 
----
  1
(1 row)

=> COMMIT;
COMMIT