Точка сохранения и вложенные транзакции

Создаем расширение и таблицу.

=> 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)

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