Jeg synes, det er et interessant spørgsmål, som fortjener et uddybende svar; tak over med mig, hvis det er lidt langt.
Kort sagt:Dit gæt er rigtigt, og du kan bruge følgende RETURNING
klausul for at bestemme, om rækken blev indsat og ikke opdateret:
RETURNERING (xmax =0) SOM indsat
Nu den detaljerede forklaring:
Når en række opdateres, ændrer PostgreSQL ikke dataene, men opretter en ny version af rækken; den gamle version vil blive slettet af autovacuum når det ikke længere er nødvendigt. En version af en række kaldes en tuple , så i PostgreSQL kan der være mere end én tupler pr. række.
xmax
tjener to forskellige formål:
-
Som det fremgår af dokumentationen, kan det være transaktions-id'et for transaktionen, der har slettet (eller opdateret) tuplen ("tuple" er et andet ord for "række"). Kun transaktioner med et transaktions-id mellem
xmin
ogxmax
kan se tupelen. En gammel tuple kan slettes sikkert, hvis der ikke er nogen transaktion med et transaktions-id mindre endxmax
. -
xmax
bruges også til at gemme rækkelåse . I PostgreSQL gemmes rækkelåse ikke i låsetabellen, men i tuple for at undgå overløb af låsetabellen.
Hvis kun én transaktion har en lås på rækken,xmax
vil indeholde transaktions-id'et for låsetransaktionen. Hvis mere end én transaktion har en lås på rækken,xmax
indeholder nummeret på en såkaldt multixact , som er en datastruktur, der igen indeholder transaktions-id'erne for låsetransaktionerne.
Dokumentationen for xmax
er ikke fuldstændig, fordi den nøjagtige betydning af dette felt betragtes som en implementeringsdetalje og kan ikke forstås uden at kende t_infomask
af tuplen, som ikke umiddelbart er synlig via SQL.
Du kan installere bidragsmodulet pageinspect
for at se dette og andre felter i en tupel.
Jeg kørte dit eksempel, og det er det, jeg ser, når jeg bruger heap_page_items
funktion til at undersøge detaljer (transaktions-id-numrene er selvfølgelig forskellige i mit tilfælde):
SELECT *, ctid, xmin, xmax FROM t;┌───┬────┬───────┬─────────────── ─ Hver │ 1 │ 11 │ (0,2) │ 102508 │ 102508 ││ 2 │ 22 │ (0,3) │ 102508 │ 0 │└ Hver ──────┴────────┘(2 rækker)VÆLG lp, lp_off, t_xmin, t_xmax, t_ctid, to_hex(t_infomask) AS t_2_page(as_t_infomaske) AS t_2_page(AS_2_side) .t', 0));┌────┬────────┬────────┬──└ ─ ovearat ─ Hver ──øre ommearats───┤│ 1 │ 8160 │ 102507 │ 102508 │ (0,2) │ 500 │ 4002 ││ 2 │ 8128 │ 102508 │ 102508 │ (0,2) │ 2190 │ 8002 ││ 3 │ 8096 │ 102508 │ 0 │ (0,3) │ 900 │ 2 │└ Hver ┴────────┴────────────┴─────────────)s kode 3
Betydningen af t_infomask
og t_infomask2
kan findes i src/include/access/htup_details.h
. lp_off
er forskydningen af tuple-dataene på siden, og t_ctid
er det nuværende tuple-id som består af sidetallet og et tupelnummer på siden. Da tabellen er nyoprettet, er alle data på side 0.
Lad mig diskutere de tre rækker, der returneres af heap_page_items
.
-
Ved linjemarkør (lp
) 1 finder vi den gamle, opdaterede tupel. Den havde oprindeligt ctid =(0,1)
, men det blev ændret til at indeholde tuple-id'et for den aktuelle version under opdateringen. Tuple blev oprettet af transaktion 102507 og ugyldiggjort af transaktion 102508 (transaktionen, der udstedte INSERT ... ON CONFLICT
). Denne tuple er ikke længere synlig og vil blive fjernet under VACUUM
.
t_infomask
viser, at både xmin
og xmax
hører til forpligtede transaktioner og viser følgelig, hvornår tuplerne blev oprettet og slettet. t_infomask2
viser, at tuppelen blev opdateret med en HOT (kun heap-tuple ) update, hvilket betyder, at den opdaterede tuple er på samme side som den originale tuple, og ingen indekseret kolonne blev ændret (se src/backend/access/heap/README.HOT
).
-
Ved linjemarkør 2 ser vi den nye, opdaterede tuple, der blev oprettet ved transaktionen INSERT ... ON CONFLICT
(transaktion 102508).
t_infomask
viser, at denne tuple er resultatet af en opdatering, xmin
er gyldig, og xmax
indeholder en KEY SHARE
rækkelås (som ikke længere er relevant, da transaktionen er gennemført). Denne rækkelås blev taget under INSERT ... ON CONFLICT
forarbejdning. t_infomask2
viser, at dette er en HOT tuple.
-
Ved linjemarkør 3 ser vi den nyindsatte række.
t_infomask
viser, at xmin
er gyldig og xmax
er ugyldig. xmax
er sat til 0, fordi denne værdi altid bruges til nyligt indsatte tupler.
Så xmax
er ikke nul af den opdaterede række er en implementeringsartefakt forårsaget af en rækkelås. Det er tænkeligt, at INSERT ... ON CONFLICT
bliver genimplementeret en dag, så denne adfærd ændrer sig, men det tror jeg er usandsynligt.