Создаем функцию, возвращающую единицу:
=> 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