これは、詳細な回答に値する興味深い質問だと思います。少し長い場合はご容赦ください。
要するに:あなたの推測は正しいです、そしてあなたは次のRETURNING
を使うことができます 行が挿入され、更新されていないかどうかを判断する句:
RETURNING (xmax = 0) AS inserted
ここで詳細な説明:
行が更新されると、PostgreSQLはデータを変更しませんが、新しいバージョンを作成します。 行の;古いバージョンはautovacuumによって削除されます 不要になったとき。行のバージョンはタプルと呼ばれます したがって、PostgreSQLでは、行ごとに複数のタプルが存在する可能性があります。
xmax
2つの異なる目的を果たします:
-
ドキュメントに記載されているように、タプルを削除(または更新)したのはトランザクションのトランザクションIDである可能性があります(「タプル」は「行」の別の単語です)。
xmin
間のトランザクションIDを持つトランザクションのみ およびxmax
タプルを見ることができます。トランザクションIDがxmax
未満のトランザクションがない場合は、古いタプルを安全に削除できます。 。 -
xmax
行ロックの保存にも使用されます 。 PostgreSQLでは、行ロックはロックテーブルに保存されませんが、ロックテーブルのオーバーフローを回避するためにタプルに保存されます。
行にロックがあるトランザクションが1つだけの場合は、xmax
ロックトランザクションのトランザクションIDが含まれます。複数のトランザクションで行がロックされている場合は、xmax
いわゆるmultixactの数が含まれています 、これは、ロックされているトランザクションのトランザクションIDを含むデータ構造です。
xmax
のドキュメント このフィールドの正確な意味は実装の詳細と見なされ、t_infomask
を知らなければ理解できないため、は完全ではありません。 タプルの、SQLですぐには表示されません。
contribモジュールpageinspect
をインストールできます タプルのこのフィールドと他のフィールドを表示します。
私はあなたの例を実行しました、そしてこれは私がheap_page_items
を使用するときに私が見るものです 詳細を調べる機能(私の場合、トランザクションID番号はもちろん異なります):
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
現在のタプルID これは、ページ番号とページ内のタプル番号で構成されます。テーブルが新しく作成されたため、すべてのデータはページ0にあります。
heap_page_items
によって返される3つの行について説明します 。
-
ラインポインタ (
lp
)1古い更新されたタプルが見つかります。元々はctid = (0,1)
でした 、ただし、更新中に現在のバージョンのタプルIDを含むように変更されました。タプルはトランザクション102507によって作成され、トランザクション102508(INSERT ... ON CONFLICT
を発行したトランザクション)によって無効化されました。 )。このタプルは表示されなくなり、VACUUM
中に削除されます 。t_infomask
両方のxmin
およびxmax
コミットされたトランザクションに属し、その結果、タプルがいつ作成および削除されたかを示します。t_infomask2
タプルがHOTで更新されたことを示します(ヒープのみのタプル )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
が考えられます この動作が変わるように、ある日再実装されますが、それはありそうもないと思います。