Ik denk dat dit een interessante vraag is die een diepgaand antwoord verdient; heb alsjeblieft geduld met me als het een beetje lang is.
In het kort:uw gok is juist, en u kunt de volgende RETURNING
. gebruiken clausule om te bepalen of de rij is ingevoegd en niet is bijgewerkt:
RETURNING (xmax = 0) AS inserted
Nu de gedetailleerde uitleg:
Wanneer een rij wordt bijgewerkt, wijzigt PostgreSQL de gegevens niet, maar maakt het een nieuwe versie van de rij; de oude versie wordt verwijderd door autovacuum wanneer het niet meer nodig is. Een versie van een rij heet een tupel , dus in PostgreSQL kunnen er meer dan één tuples per rij zijn.
xmax
heeft twee verschillende doelen:
-
Zoals vermeld in de documentatie, kan het de transactie-ID van de transactie zijn die de tuple heeft verwijderd (of bijgewerkt) (“tuple” is een ander woord voor “rij”). Alleen transacties met een transactie-ID tussen
xmin
enxmax
kan de tupel zien. Een oude tuple kan veilig worden verwijderd als er geen transactie is met een transactie-ID kleiner danxmax
. -
xmax
wordt ook gebruikt om rijvergrendelingen op te slaan . In PostgreSQL worden rijvergrendelingen niet opgeslagen in de vergrendelingstabel, maar in de tupel om overloop van de vergrendelingstabel te voorkomen.
Als slechts één transactie een vergrendeling op de rij heeft,xmax
bevat de transactie-ID van de vergrendelingstransactie. Als meer dan één transactie een slot op de rij heeft,xmax
bevat het nummer van een zogenaamde multixact , wat een gegevensstructuur is die op zijn beurt de transactie-ID's van de sluittransacties bevat.
De documentatie van xmax
is niet volledig, omdat de exacte betekenis van dit veld wordt beschouwd als een implementatiedetail en niet kan worden begrepen zonder t_infomask
te kennen van de tuple, die niet direct zichtbaar is via SQL.
U kunt de bijdragemodule pageinspect
. installeren om deze en andere velden van een tuple te bekijken.
Ik heb je voorbeeld uitgevoerd en dit is wat ik zie als ik de heap_page_items
gebruik functie om details te onderzoeken (de transactie-ID-nummers zijn natuurlijk anders in mijn geval):
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)
De betekenis van t_infomask
en t_infomask2
is te vinden in src/include/access/htup_details.h
. lp_off
is de offset van de tuple-gegevens in de pagina, en t_ctid
is de huidige tuple-ID die bestaat uit het paginanummer en een tupelnummer binnen de pagina. Omdat de tabel nieuw is gemaakt, staan alle gegevens op pagina 0.
Laat me de drie rijen bespreken die worden geretourneerd door heap_page_items
.
-
Bij regelaanwijzer (
lp
) 1 vinden we de oude, bijgewerkte tuple. Het had oorspronkelijkctid = (0,1)
, maar dat werd tijdens de update gewijzigd om de tuple-ID van de huidige versie te bevatten. De Tuple is gemaakt door transactie 102507 en ongeldig gemaakt door transactie 102508 (de transactie die deINSERT ... ON CONFLICT
heeft uitgegeven ). Deze tuple is niet meer zichtbaar en wordt verwijderd tijdensVACUUM
.t_infomask
laat zien dat zowelxmin
enxmax
behoren tot vastgelegde transacties en laten dus zien wanneer de tuples zijn gemaakt en verwijderd.t_infomask2
laat zien dat de tuple is bijgewerkt met een HOT (alleen heap tuple .) ) update, wat betekent dat de bijgewerkte tuple op dezelfde pagina staat als de originele tuple en dat er geen geïndexeerde kolom is gewijzigd (ziesrc/backend/access/heap/README.HOT
). -
Bij regelaanwijzer 2 zien we de nieuwe, bijgewerkte tuple die is gemaakt door transactie de
INSERT ... ON CONFLICT
(transactie 102508).t_infomask
laat zien dat deze tuple het resultaat is van een update,xmin
geldig is, enxmax
bevat eenKEY SHARE
rijslot (wat niet langer relevant is sinds de transactie is voltooid). Dit rijslot is genomen tijdensINSERT ... ON CONFLICT
verwerken.t_infomask2
laat zien dat dit een HEET tuple is. -
Bij lijnaanwijzer 3 zien we de nieuw ingevoegde rij.
t_infomask
laat zien datxmin
is geldig enxmax
is ongeldig.xmax
is ingesteld op 0 omdat deze waarde altijd wordt gebruikt voor nieuw ingevoegde tuples.
Dus de niet-nul xmax
van de bijgewerkte rij is een implementatieartefact veroorzaakt door een rijvergrendeling. Het is denkbaar dat INSERT ... ON CONFLICT
wordt op een dag opnieuw geïmplementeerd zodat dit gedrag verandert, maar ik denk dat dat onwaarschijnlijk is.