Penso che questa sia una domanda interessante che meriti una risposta approfondita; per favore, abbi pazienza se è un po' lungo.
In breve:la tua ipotesi è giusta e puoi utilizzare il seguente RETURNING
clausola per determinare se la riga è stata inserita e non aggiornata:
RETURNING (xmax = 0) AS inserted
Ora la spiegazione dettagliata:
Quando una riga viene aggiornata, PostgreSQL non modifica i dati, ma crea una nuova versione della fila; la vecchia versione verrà eliminata da autovacuum quando non serve più. Una versione di una riga è chiamata tupla , quindi in PostgreSQL possono esserci più tuple per riga.
xmax
ha due scopi diversi:
-
Come indicato nella documentazione, può essere l'ID transazione della transazione che ha eliminato (o aggiornato) la tupla ("tupla" è un'altra parola per "riga"). Solo transazioni con un ID transazione tra
xmin
exmax
può vedere la tupla. Una vecchia tupla può essere eliminata in modo sicuro se non ci sono transazioni con un ID transazione inferiore axmax
. -
xmax
viene utilizzato anche per memorizzare blocchi di riga . In PostgreSQL, i blocchi di riga non sono archiviati nella tabella di blocco, ma nella tupla per evitare l'overflow della tabella di blocco.
Se solo una transazione ha un blocco sulla riga,xmax
conterrà l'ID transazione della transazione di blocco. Se più di una transazione ha un blocco sulla riga,xmax
contiene il numero di un cosiddetto multixact , che è una struttura di dati che a sua volta contiene gli ID transazione delle transazioni di blocco.
La documentazione di xmax
non è completo, perché il significato esatto di questo campo è considerato un dettaglio di implementazione e non può essere compreso senza conoscere t_infomask
della tupla, che non è immediatamente visibile tramite SQL.
Puoi installare il modulo di contributo pageinspect
per visualizzare questo e altri campi di una tupla.
Ho eseguito il tuo esempio e questo è ciò che vedo quando utilizzo heap_page_items
funzione per esaminare i dettagli (i numeri ID transazione sono ovviamente diversi nel mio caso):
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)
I significati di t_infomask
e t_infomask2
può essere trovato in src/include/access/htup_details.h
. lp_off
è l'offset dei dati della tupla nella pagina e t_ctid
è l'ID tupla corrente che consiste nel numero di pagina e un numero tupla all'interno della pagina. Poiché la tabella è stata appena creata, tutti i dati sono nella pagina 0.
Consentitemi di discutere le tre righe restituite da heap_page_items
.
-
Al puntatore di linea (
lp
) 1 troviamo la vecchia tupla aggiornata. Originariamente avevactid = (0,1)
, ma è stato modificato per contenere l'ID tupla della versione corrente durante l'aggiornamento. La Tuple è stata creata dalla transazione 102507 e invalidata dalla transazione 102508 (la transazione che ha emesso ilINSERT ... ON CONFLICT
). Questa tupla non è più visibile e verrà rimossa duranteVACUUM
.t_infomask
mostra che entrambixmin
exmax
appartengono a transazioni confermate e di conseguenza mostrano quando le tuple sono state create ed eliminate.t_infomask2
mostra che la tupla è stata aggiornata con una HOT (tupla solo heap ) update, il che significa che la tupla aggiornata si trova nella stessa pagina della tupla originale e non è stata modificata alcuna colonna indicizzata (vedisrc/backend/access/heap/README.HOT
). -
Al puntatore di riga 2 vediamo la nuova tupla aggiornata creata dalla transazione
INSERT ... ON CONFLICT
(transazione 102508).t_infomask
mostra che questa tupla è il risultato di un aggiornamento,xmin
è valido exmax
contiene unKEY SHARE
blocco riga (che non è più rilevante poiché la transazione è stata completata). Questo blocco riga è stato eseguito duranteINSERT ... ON CONFLICT
in lavorazione.t_infomask2
mostra che questa è una tupla HOT. -
Al puntatore di riga 3 vediamo la riga appena inserita.
t_infomask
mostra chexmin
è valido exmax
è invalido.xmax
è impostato su 0 perché questo valore viene sempre utilizzato per le tuple appena inserite.
Quindi il diverso da zero xmax
della riga aggiornata è un artefatto di implementazione causato da un blocco di riga. È ipotizzabile che INSERT ... ON CONFLICT
viene reimplementato un giorno in modo che questo comportamento cambi, ma penso che sia improbabile.