Myślę, że to ciekawe pytanie, które zasługuje na dogłębną odpowiedź; proszę o wyrozumiałość, jeśli jest trochę za długi.
W skrócie:Twoje przypuszczenie jest prawidłowe i możesz użyć następującego RETURNING
klauzula określająca, czy wiersz został wstawiony, a nie zaktualizowany:
RETURNING (xmax = 0) AS inserted
Teraz szczegółowe wyjaśnienie:
Gdy wiersz jest aktualizowany, PostgreSQL nie modyfikuje danych, ale tworzy nową wersję rzędu; stara wersja zostanie usunięta przez autovacuum kiedy nie jest już potrzebny. Wersja wiersza nazywana jest krotką , więc w PostgreSQL może być więcej niż jedna krotka w wierszu.
xmax
służy dwóm różnym celom:
-
Jak stwierdzono w dokumentacji, może to być identyfikator transakcji, która usunęła (lub zaktualizowała) krotkę („krotka” to inne słowo oznaczające „wiersz”). Tylko transakcje z identyfikatorem transakcji między
xmin
ixmax
widzi krotkę. Starą krotkę można bezpiecznie usunąć, jeśli nie ma transakcji o identyfikatorze transakcji mniejszym niżxmax
. -
xmax
służy również do przechowywania blokad wierszy . W PostgreSQL blokady wierszy nie są przechowywane w tabeli blokad, ale w krotce, aby uniknąć przepełnienia tabeli blokad.
Jeśli tylko jedna transakcja ma blokadę w wierszu,xmax
będzie zawierać identyfikator transakcji blokującej. Jeśli więcej niż jedna transakcja ma blokadę w wierszu,xmax
zawiera numer tak zwanego multixact , która jest strukturą danych, która z kolei zawiera identyfikatory transakcji blokujących.
Dokumentacja xmax
nie jest kompletne, ponieważ dokładne znaczenie tego pola jest uważane za szczegół implementacji i nie można go zrozumieć bez znajomości t_infomask
krotki, która nie jest od razu widoczna przez SQL.
Możesz zainstalować moduł contrib pageinspect
aby zobaczyć to i inne pola krotki.
Uruchomiłem twój przykład i to właśnie widzę, gdy używam heap_page_items
funkcja do zbadania szczegółów (numery ID transakcji są oczywiście inne w moim przypadku):
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)
Znaczenia t_infomask
i t_infomask2
można znaleźć w src/include/access/htup_details.h
. lp_off
jest przesunięciem danych krotki na stronie, a t_ctid
to aktualny identyfikator krotki który składa się z numeru strony i numeru krotki na stronie. Ponieważ tabela została nowo utworzona, wszystkie dane znajdują się na stronie 0.
Pozwól, że omówię trzy wiersze zwrócone przez heap_page_items
.
-
W wskaźniku linii (
lp
) 1 znajdujemy starą, zaktualizowaną krotkę. Pierwotnie miałctid = (0,1)
, ale został zmodyfikowany tak, aby zawierał identyfikator krotki bieżącej wersji podczas aktualizacji. Krotka została utworzona przez transakcję 102507 i unieważniona przez transakcję 102508 (transakcja, która wysłałaINSERT ... ON CONFLICT
). Ta krotka nie jest już widoczna i zostanie usunięta podczasVACUUM
.t_infomask
pokazuje, że obaxmin
ixmax
należą do zatwierdzonych transakcji i w konsekwencji pokazują, kiedy krotki zostały utworzone i usunięte.t_infomask2
pokazuje, że krotka została zaktualizowana za pomocą HOT (tylko krotka sterty ) update, co oznacza, że zaktualizowana krotka znajduje się na tej samej stronie co oryginalna krotka i żadna indeksowana kolumna nie została zmodyfikowana (zobaczsrc/backend/access/heap/README.HOT
). -
W wierszu wskaźnika 2 widzimy nową, zaktualizowaną krotkę utworzoną przez transakcję
INSERT ... ON CONFLICT
(transakcja 102508).t_infomask
pokazuje, że ta krotka jest wynikiem aktualizacji,xmin
jest prawidłowy, axmax
zawieraKEY SHARE
blokada wiersza (która nie ma już znaczenia od czasu zakończenia transakcji). Ta blokada wiersza została zajęta podczasINSERT ... ON CONFLICT
przetwarzanie.t_infomask2
pokazuje, że jest to GORĄCA krotka. -
Przy wskaźniku linii 3 widzimy nowo wstawiony wiersz.
t_infomask
pokazuje, żexmin
jest prawidłowy ixmax
jest nieważny.xmax
jest ustawiona na 0, ponieważ ta wartość jest zawsze używana dla nowo wstawianych krotek.
Więc niezerowy xmax
zaktualizowanego wiersza to artefakt implementacji spowodowany blokadą wiersza. Można sobie wyobrazić, że INSERT ... ON CONFLICT
zostanie ponownie wdrożony pewnego dnia, aby to zachowanie się zmieniło, ale myślę, że jest to mało prawdopodobne.