Создаем расширение и таблицу.
=> CREATE DATABASE mvcc_tuples;
CREATE DATABASE
=> \c mvcc_tuples
You are now connected to database "mvcc_tuples" as user "postgres".
=> CREATE EXTENSION pageinspect;
CREATE EXTENSION
=> CREATE TABLE t(s text);
CREATE TABLE
Создадим представление, аналогичное тому, которое использовалось в демонстрации:
=> CREATE VIEW t_v AS SELECT '(0,'||lp||')' AS ctid, CASE lp_flags WHEN 0 THEN 'unused' WHEN 1 THEN 'normal' WHEN 2 THEN 'redirect to '||lp_off WHEN 3 THEN 'dead' END AS state, t_xmin as xmin, t_xmax as xmax, CASE WHEN (t_infomask & 256) > 0 THEN 't' END AS xmin_c, CASE WHEN (t_infomask & 512) > 0 THEN 't' END AS xmin_a, CASE WHEN (t_infomask & 1024) > 0 THEN 't' END AS xmax_c, CASE WHEN (t_infomask & 2048) > 0 THEN 't' END AS xmax_a, t_ctid FROM heap_page_items(get_raw_page('t',0)) ORDER BY lp;
CREATE VIEW
Начинаем транзакцию.
=> BEGIN;
BEGIN
Вставляем первую строку.
=> INSERT INTO t VALUES ('first');
INSERT 0 1
=> SELECT txid_current();
txid_current -------------- 55462 (1 row)
=> SELECT *, xmin, xmax FROM t;
s | xmin | xmax -------+-------+------ first | 55462 | 0 (1 row)
=> SELECT * FROM t_v;
ctid | state | xmin | xmax | xmin_c | xmin_a | xmax_c | xmax_a | t_ctid -------+--------+-------+------+--------+--------+--------+--------+-------- (0,1) | normal | 55462 | 0 | | | | t | (0,1) (1 row)
Точка сохранения и вторая строка.
=> SAVEPOINT sp;
SAVEPOINT
=> INSERT INTO t VALUES ('second');
INSERT 0 1
=> SELECT txid_current();
txid_current -------------- 55462 (1 row)
Заметьте: функция txid_current() выдает номер основной, а не вложенной, транзакции.
=> SELECT *, xmin, xmax FROM t;
s | xmin | xmax --------+-------+------ first | 55462 | 0 second | 55463 | 0 (2 rows)
=> SELECT * FROM t_v;
ctid | state | xmin | xmax | xmin_c | xmin_a | xmax_c | xmax_a | t_ctid -------+--------+-------+------+--------+--------+--------+--------+-------- (0,1) | normal | 55462 | 0 | | | | t | (0,1) (0,2) | normal | 55463 | 0 | | | | t | (0,2) (2 rows)
Откат и вставка третьей строки.
=> ROLLBACK TO sp;
ROLLBACK
=> INSERT INTO t VALUES ('third');
INSERT 0 1
=> SELECT txid_current();
txid_current -------------- 55462 (1 row)
=> SELECT *, xmin, xmax FROM t;
s | xmin | xmax -------+-------+------ first | 55462 | 0 third | 55464 | 0 (2 rows)
=> SELECT * FROM t_v;
ctid | state | xmin | xmax | xmin_c | xmin_a | xmax_c | xmax_a | t_ctid -------+--------+-------+------+--------+--------+--------+--------+-------- (0,1) | normal | 55462 | 0 | | | | t | (0,1) (0,2) | normal | 55463 | 0 | | t | | t | (0,2) (0,3) | normal | 55464 | 0 | | | | t | (0,3) (3 rows)
Заметьте: в странице продолжаем видеть строку, добавленную отмененной вложенной транзакцией.
Фиксация изменений.
=> COMMIT;
COMMIT
=> SELECT *, xmin, xmax FROM t;
s | xmin | xmax -------+-------+------ first | 55462 | 0 third | 55464 | 0 (2 rows)
=> SELECT * FROM t_v;
ctid | state | xmin | xmax | xmin_c | xmin_a | xmax_c | xmax_a | t_ctid -------+--------+-------+------+--------+--------+--------+--------+-------- (0,1) | normal | 55462 | 0 | t | | | t | (0,1) (0,2) | normal | 55463 | 0 | | t | | t | (0,2) (0,3) | normal | 55464 | 0 | t | | | t | (0,3) (3 rows)
Видим, что каждая вложенная транзакция имеет собственный статус.