Мисля, че това е интересен въпрос, който заслужава задълбочен отговор; моля, потърпете, ако е малко продължително.
Накратко:Вашето предположение е правилно и можете да използвате следния RETURNING
клауза, за да се определи дали редът е бил вмъкнат и не е актуализиран:
RETURNING (xmax = 0) AS inserted
Сега подробното обяснение:
Когато един ред се актуализира, PostgreSQL не променя данните, а създава нова версия на реда; старата версия ще бъде изтрита чрез autovacuum когато вече не е необходимо. Версия на ред се нарича кортежка , така че в PostgreSQL може да има повече от един кортеж на ред.
xmax
служи за две различни цели:
-
Както е посочено в документацията, това може да бъде идентификаторът на транзакцията на транзакцията, която е изтрила (или актуализира) кортежа („кортежа“ е друга дума за „ред“). Само транзакции с идентификатор на транзакция между
xmin
иxmax
може да види кортежа. Стар кортеж може да бъде изтрит безопасно, ако няма транзакция с идентификатор на транзакция по-малък отxmax
. -
xmax
се използва също за съхраняване на ключалки на редове . В PostgreSQL заключванията на редове не се съхраняват в таблицата за заключване, а в кортежа, за да се избегне препълване на таблицата за заключване.
Ако само една транзакция има заключване на реда,xmax
ще съдържа идентификатора на транзакцията на заключващата транзакция. Ако повече от една транзакция има заключване на реда,xmax
съдържа номера на така наречения мултиакт , което е структура от данни, която от своя страна съдържа идентификаторите на транзакциите на заключващите транзакции.
Документацията на xmax
не е пълно, тъй като точното значение на това поле се счита за детайл от изпълнението и не може да бъде разбрано без да се знае t_infomask
на кортежа, който не се вижда веднага чрез SQL.
Можете да инсталирате модула contrib pageinspect
за да видите това и други полета на кортеж.
Пуснах вашия пример и това виждам, когато използвам heap_page_items
функция за проверка на подробностите (разбира се, идентификационните номера на транзакциите са различни в моя случай):
SELECT *, ctid, xmin, xmax FROM t;
┌───┬────┬───────┬────────┬────────┐
│ i │ x │ ctid │ xmin │ xmax │
├───┼────┼───────┼────────┼────────┤
│ 1 │ 11 │ (0,2) │ 102508 │ 102508 │
│ 2 │ 22 │ (0,3) │ 102508 │ 0 │
└───┴────┴───────┴────────┴────────┘
(2 rows)
SELECT lp, lp_off, t_xmin, t_xmax, t_ctid,
to_hex(t_infomask) AS t_infomask, to_hex(t_infomask2) AS t_infomask2
FROM heap_page_items(get_raw_page('laurenz.t', 0));
┌────┬────────┬────────┬────────┬────────┬────────────┬─────────────┐
│ lp │ lp_off │ t_xmin │ t_xmax │ t_ctid │ t_infomask │ t_infomask2 │
├────┼────────┼────────┼────────┼────────┼────────────┼─────────────┤
│ 1 │ 8160 │ 102507 │ 102508 │ (0,2) │ 500 │ 4002 │
│ 2 │ 8128 │ 102508 │ 102508 │ (0,2) │ 2190 │ 8002 │
│ 3 │ 8096 │ 102508 │ 0 │ (0,3) │ 900 │ 2 │
└────┴────────┴────────┴────────┴────────┴────────────┴─────────────┘
(3 rows)
Значенията на t_infomask
и t_infomask2
може да се намери в src/include/access/htup_details.h
. lp_off
е отместването на кортежните данни в страницата и t_ctid
е текущият идентификатор на кортежа който се състои от номера на страницата и номер на кортежа в страницата. Тъй като таблицата е новосъздадена, всички данни са на страница 0.
Позволете ми да обсъдя трите реда, върнати от heap_page_items
.
-
В указател на линия (
lp
) 1 намираме стария, актуализиран кортеж. Първоначално имашеctid = (0,1)
, но това беше променено, за да съдържа идентификатора на кортежа на текущата версия по време на актуализацията. Кортежът е създаден от транзакция 102507 и невалиден от транзакция 102508 (транзакцията, която е издалаINSERT ... ON CONFLICT
). Този кортеж вече не се вижда и ще бъде премахнат по време наVACUUM
.t_infomask
показва, че и дветеxmin
иxmax
принадлежат към извършени транзакции и следователно показват кога кортежите са създадени и изтрити.t_infomask2
показва, че кортежът е актуализиран с HOT (кортеж само за heap ) update, което означава, че актуализираният кортеж е на същата страница като оригиналния кортеж и нито една индексирана колона не е променена (вижтеsrc/backend/access/heap/README.HOT
). -
В указател на ред 2 виждаме новия, актуализиран кортеж, който е създаден чрез транзакция
INSERT ... ON CONFLICT
(транзакция 102508).t_infomask
показва, че този кортеж е резултат от актуализация,xmin
е валиден иxmax
съдържаKEY SHARE
заключване на редове (което вече не е от значение, след като транзакцията е приключила). Това заключване на ред е взето по време наINSERT ... ON CONFLICT
обработка.t_infomask2
показва, че това е HOT кортеж. -
При указател на ред 3 виждаме нововмъкнатия ред.
t_infomask
показва, чеxmin
е валиден иxmax
е невалидно.xmax
е зададено на 0, защото тази стойност винаги се използва за нововмъкнати кортежи.
Така че ненулевият xmax
на актуализирания ред е артефакт на реализацията, причинен от заключване на ред. Възможно е INSERT ... ON CONFLICT
се прилага отново един ден, така че това поведение да се промени, но мисля, че това е малко вероятно.