<productname>PostgreSQL</productname> enforces SQL uniqueness constraints
using <firstterm>unique indexes</firstterm>, which are indexes that disallow
multiple entries with identical keys. An access method that supports this
feature sets <structfield>amcanunique</structfield> true.
(At present, only b-tree supports it.) Columns listed in the
<literal>INCLUDE</literal> clause are not considered when enforcing
uniqueness.
PostgreSQLは、SQLの一意性制約を一意性インデックスを使用して強制します。
このインデックスでは、同一キーに対し複数の項目を許しません。
この機能をサポートするアクセスメソッドはamcanunique
を真に設定します。
(現時点ではb-treeのみがこれをサポートします。)
INCLUDE
句内の列のリストは、一意性制約の強制時には考慮されません。
Because of MVCC, it is always necessary to allow duplicate entries to exist physically in an index: the entries might refer to successive versions of a single logical row. The behavior we actually want to enforce is that no MVCC snapshot could include two rows with equal index keys. This breaks down into the following cases that must be checked when inserting a new row into a unique index: MVCCのため、インデックス内に物理的に重複した項目が存在できることが常に必要です。 これらの項目は1つの論理的な行の連続的なバージョンを示します。 実際に強制させたい動作は、MVCCスナップショットが同じインデックスキーを持つ行を2つ含めないことです。 一意性インデックスに新しい行を挿入する時に検査しなければならない状況を以下のように分割することができます。
If a conflicting valid row has been deleted by the current transaction, it's okay. (In particular, since an UPDATE always deletes the old row version before inserting the new version, this will allow an UPDATE on a row without changing the key.) 競合する有効な行が現在のトランザクションで削除された場合は問題ありません。 (具体的には、UPDATEは常に新しいバージョンを挿入する前に古い行バージョンを削除します。 これによりキーを変更することなく行をUPDATEすることができます。)
If a conflicting row has been inserted by an as-yet-uncommitted transaction, the would-be inserter must wait to see if that transaction commits. If it rolls back then there is no conflict. If it commits without deleting the conflicting row again, there is a uniqueness violation. (In practice we just wait for the other transaction to end and then redo the visibility check in toto.) 競合する行が未コミットのトランザクションで挿入された場合、挿入しようとしている方はトランザクションのコミットが分かるまで待機しなければなりません。 ロールバックした場合は競合しません。 競合する行が削除されずにコミットした場合、一意性違反となります。 (具体的には、他のトランザクションの終了をただ待機し、終了後に可視性の検査を完全に再実行します。)
Similarly, if a conflicting valid row has been deleted by an as-yet-uncommitted transaction, the would-be inserter must wait for that transaction to commit or abort, and then repeat the test. 同様に、競合する有効な行が未コミットのトランザクションで削除された場合、挿入しようとしている方はトランザクションのコミットまたはアボートを待機しなければならず、その後、試験を繰り返します。
Furthermore, immediately before reporting a uniqueness violation
according to the above rules, the access method must recheck the
liveness of the row being inserted. If it is committed dead then
no violation should be reported. (This case cannot occur during the
ordinary scenario of inserting a row that's just been created by
the current transaction. It can happen during
<command>CREATE UNIQUE INDEX CONCURRENTLY</command>, however.)
さらに、上記規則に従った一意性違反を報告する直前に、アクセスメソッドは挿入される行の有効性を再度検査しなければなりません。
もし、無効なコミットであれば、違反を報告してはいけません。
(現在のトランザクションによって作成された通常の行の挿入という状況では、これは発生することはありません。
しかし、これはCREATE UNIQUE INDEX CONCURRENTLY
中に発生することがあります。)
We require the index access method to apply these tests itself, which means that it must reach into the heap to check the commit status of any row that is shown to have a duplicate key according to the index contents. This is without a doubt ugly and non-modular, but it saves redundant work: if we did a separate probe then the index lookup for a conflicting row would be essentially repeated while finding the place to insert the new row's index entry. What's more, there is no obvious way to avoid race conditions unless the conflict check is an integral part of insertion of the new index entry. インデックスアクセスメソッドにこうした試験を自身で行うことを要求します。 これは、インデックスの内容に対して重複するキーを持つことを示している任意の行のコミット状態を検査するために、ヒープまでアクセスしなければならないことを意味します。 これが醜くモジュール化されないことには疑う余地はありません。 しかし、余計な作業を防ぐことができます。 もし分離された探査を行ったとすると、新しいインデックス項目を挿入する場所を検索する時、競合する行に対するインデックス検索がどうしても繰り返されます。 さらに、競合検査がインデックス行の挿入部分で統合されて行われない限り、競合状態を防ぐ明確な方法がありません。
If the unique constraint is deferrable, there is additional complexity:
we need to be able to insert an index entry for a new row, but defer any
uniqueness-violation error until end of statement or even later. To
avoid unnecessary repeat searches of the index, the index access method
should do a preliminary uniqueness check during the initial insertion.
If this shows that there is definitely no conflicting live tuple, we
are done. Otherwise, we schedule a recheck to occur when it is time to
enforce the constraint. If, at the time of the recheck, both the inserted
tuple and some other tuple with the same key are live, then the error
must be reported. (Note that for this purpose, <quote>live</quote> actually
means <quote>any tuple in the index entry's HOT chain is live</quote>.)
To implement this, the <function>aminsert</function> function is passed a
<literal>checkUnique</literal> parameter having one of the following values:
一意性制約が遅延可能である場合はさらに複雑になります。
新しい行向けのインデックス項目を挿入可能にする必要があります。
しかし一意性違反エラーは文の終わりまたはそれ以降まで遅延されます。
不要なインデックス検索の繰り返しを防ぐために、インデックスアクセスメソッドは初期の挿入の間に前座の一意性検査を行わなければなりません。
これが現存するタプルとまったく競合がないことを示した場合、それで終了です。
さもなければ、制約を強制する時に再検査を行うようスケジュールします。
再検査の時点で対象のタプルと同じキーを持つ何らかの他のタプルが存在すると、エラーを報告しなければなりません。
(この目的のために「存在する」は実際には「インデックス項目のHOTチェイン内に何らかのタプルが存在する」ことを意味します。)
これを実装するために、aminsert
は以下のいずれかの値を持つcheckUnique
パラメータを渡されます。
<literal>UNIQUE_CHECK_NO</literal> indicates that no uniqueness checking
should be done (this is not a unique index).
UNIQUE_CHECK_NO
は、一意性検査を行うことはない(これは一意性インデックスではない)ことを示します。
<literal>UNIQUE_CHECK_YES</literal> indicates that this is a non-deferrable
unique index, and the uniqueness check must be done immediately, as
described above.
UNIQUE_CHECK_YES
は、上述の通り遅延がない一意性インデックスであり、一意性検査を即時に行わなければならないことを示します。
<literal>UNIQUE_CHECK_PARTIAL</literal> indicates that the unique
constraint is deferrable. <productname>PostgreSQL</productname>
will use this mode to insert each row's index entry. The access
method must allow duplicate entries into the index, and report any
potential duplicates by returning false from <function>aminsert</function>.
For each row for which false is returned, a deferred recheck will
be scheduled.
UNIQUE_CHECK_PARTIAL
は一意性制約が遅延可能であることを示します。
PostgreSQLはこのモードを使用して、各行のインデックス項目を挿入します。
このアクセスメソッドはインデックス内の重複する項目を許さなければなりません。
そしてaminsert
から偽を返すことで重複の可能性があることを報告しなければなりません。
偽が返された行それぞれに対して、遅延再検査が予定されます。
The access method must identify any rows which might violate the unique constraint, but it is not an error for it to report false positives. This allows the check to be done without waiting for other transactions to finish; conflicts reported here are not treated as errors and will be rechecked later, by which time they may no longer be conflicts. アクセスメソッドは一意性制約違反となるかもしれない行を識別しなければなりません。 しかし間違った偽を報告することはエラーではありません。 これにより他のトランザクションを待つことなく検査を行うことができます。 ここで報告された重複はエラーとして扱われず、後で再検査されます。 再検査時には重複しなくなっている可能性があります。
<literal>UNIQUE_CHECK_EXISTING</literal> indicates that this is a deferred
recheck of a row that was reported as a potential uniqueness violation.
Although this is implemented by calling <function>aminsert</function>, the
access method must <emphasis>not</emphasis> insert a new index entry in this
case. The index entry is already present. Rather, the access method
must check to see if there is another live index entry. If so, and
if the target row is also still live, report error.
UNIQUE_CHECK_EXISTING
は、一意性違反の可能性があると報告された行に対する遅延再検査であることを示します。
これはaminsert
を呼び出すことで実装されますが、アクセスメソッドはこの場合に新しいインデックス項目を挿入してはいけません。
インデックス項目はすでに存在します。
それよりも、アクセスメソッドは他に存在するインデックス項目があるか検査する必要があります。
もし存在し、対象の行もまだ存在する場合エラーを報告します。
It is recommended that in a <literal>UNIQUE_CHECK_EXISTING</literal> call,
the access method further verify that the target row actually does
have an existing entry in the index, and report error if not. This
is a good idea because the index tuple values passed to
<function>aminsert</function> will have been recomputed. If the index
definition involves functions that are not really immutable, we
might be checking the wrong area of the index. Checking that the
target row is found in the recheck verifies that we are scanning
for the same tuple values as were used in the original insertion.
UNIQUE_CHECK_EXISTING
呼び出しでは、アクセスメソッドはさらに対象行が実際にインデックス内に既存の項目を持つか検証し、もしなければエラーを報告することを推奨します。
aminsert
に渡されるインデックスタプル値が再計算されているため勧めます。
インデックス定義に実際には不変ではない関数が含まれる場合、インデックスの間違った領域を検査してしまうかもしれません。
再検査にて対象行の存在を検査することで、元の挿入で使用されたものと同じタプル値をスキャンしていることを検証します。