PostgreSQLは、標準的なbtree(multi-way balanced tree)インデックスデータ構造を実装しています。 明確に定義された線形順にソート可能なデータ型は、すべてbtreeインデックスで索引付できます。 唯一の制限は、一つのインデックスエントリが(適用可能であれば、TOAST圧縮後)ページの約1/3を超えられないことです。
btree演算子クラスはそのデータ型がソート順を持つことが必要なので、btree演算子クラス(実際には演算子族)は、PostgreSQLの一般的表現として、およびソートセマンティクスを理解するものとして利用されてきました。 ですから、単にbtreeインデックスをサポートするだけに必要なもの以上の機能と、btree AMが使用するものからはかけ離れたシステムの部品を備えなければなりません。
表 36.3で示すように、btree演算子クラスは次の5つの比較演算子を提供しなければなりません。
<
、<=
、=
、>=
、そして>
です。
<>
も演算子クラスの一部であると期待する方もいるかもしれませんが、そうではありません。
インデックス検索のWHERE句で<>
を使うのは、ほとんど常に役に立たないからです。
(ある種の目的のためにプランナは<>
をbtree演算子クラスに関連しているものとして扱います。
しかし、プランナはpg_amop
から検索するのではなく=
の否定子リンクから検索します。)
複数のデータ型がほとんど同じソートセマンティクスを共有している場合、それらの演算子クラスは演算子族にまとめることができます。 そうすることによりプランナが型をまたがる比較を推論できるので、これはメリットがあります。 演算子族内の各演算子クラスは、入力データ型のための単一型演算子(および関連するサポート関数)を含むべきです。 一方型をまたがる比較演算子とサポート関数は演算子族中で「ゆるやか」です。 プランナが推移関係から推論するすべての比較条件を提示できるように、型をまたがる演算子の完全な集合を演算子族に入れておくことをお勧めします。
btree演算子族が満たさなければならない基本的な前提条件があります。
=
演算子は等号関係でなければなりません。
つまり、そのデータ型のすべての非NULL値A
、B
、C
について、
A
=
A
が真である(反射律)
A
=
B
なら、B
=
A
である(対称律)
A
=
B
かつB
=
C
なら、A
=
C
である(推移律)
<
は強順序関係でなければなりません。つまり、すべての非NULL値A
、B
、C
に対して、
A
<
A
は偽である(非反射律)
A
<
B
かつB
<
C
なら、A
<
C
である(推移律)
更に、順序は全である。すなわち、すべての非NULL値A
、B
に対して、
厳密にA
<
B
、A
=
B
、B
<
A
のうちどれか一つが真(三分律)
(もちろん、三分律は比較サポート関数の定義を正当化します。)
他の3つの演算子は=
と<
に沿って自明に定義され、それらと一貫していなければなりません。
複数のデータ型をサポートする演算子族について、演算子族中のデータ型であるどんなA
、B
、C
も上記の法則を満たさなければなりません。
型をまたがる際に2つあるいは3つの異なる演算子が一貫していることを表明することになるので、推移律を満たすことはもっとも困難です。
例をあげると、少なくともfloat8
と比較するためにnumeric
値をfloat8
に変換する現在の意味論のもとでは、float8
とnumeric
を同じ演算子族に加えるのはうまくいかないでしょう。
float8
の精度に限りがあるからです。
これは同じfloat8
値に対して等号比較する複数の異なるnumeric
値が存在することを意味し、したがって推移律は満たされません。
複数データ型族に関する別な要件は、演算子族に含まれるデータ型間に定義される暗黙的あるいは二値型強制(binary-coercion)キャストは、関係するソート順を変更してはならないことです。
単一のデータ型において、btreeインデックスがこれらの法則を守ることを要求するのはかなり明確です。 これらの法則なしにはキー並べる順序がなくなってしまうからです。 また、異なるデータ型の比較キーを使うインデックス検索では、2つのデータ型またがる比較が正常に動作することが必要です。 演算子族中で3つ以上のデータ型に対する拡張はbtreeインデックスの機構自体では要求されませんが、プランナは最適化の目的でそれらに依存します。
表 36.9で示すように、btreeでは一つの必須サポート関数と、4つの省略可能なサポート関数を定義します。 5つのユーザ定義メソッドは以下の通りです。
order
btreeの演算子族が比較演算子を提供する各データ型の組み合わせに対して、比較サポート関数を提供しなければなりません。それらはサポート関数1番でpg_amproc
に、また、比較での左右のデータ型と等しいamproclefttype
/amprocrighttype
に、登録されます(すなわち、pg_amop
に登録されている演算子が対応するものと同じデータ型です)。
比較関数は2つの非NULL値A
とB
を取り、
A
<
B
、A
=
B
、または、A
>
B
であるときにそれぞれ、<
0
、0
、または、>
0
であるint32
の値を返さなければなりません。
NULLの結果は許されず、データ型の全ての値は比較可能でなければなりません。
例としてsrc/backend/access/nbtree/nbtcompare.c
を参照してください。
比較される値が照合順序が適用可能なデータ型のものである場合、比較サポート関数に適切な照合順序のOIDが渡され、標準のPG_GET_COLLATION()
機構が使用されます。
sortsupport
任意で、btree演算子族はソートサポート関数を提供してもよいです。これはサポート関数2番で登録されます。
この関数は、素朴に比較サポート関数を呼び出すよりも、ソート目的により効果的な方法での比較の実装を可能にします。
これに関するAPIはsrc/include/utils/sortsupport.h
で定義されています。
in_range
任意で、btree演算子族はin_rangeサポート関数を提供してもよいです。これはサポート関数3番に登録されます。
これはbtreeインデックス操作中には使われません。そうではなく、演算子族のセマンティクスをRANGE
offset
PRECEDING
とRANGE
offset
FOLLOWING
フレーム境界タイプ(4.2.8を参照)を含むWINDOW句に対応できるように拡張します。
基本的には、提供される拡張情報はどのように演算子族のデータ並び順と互換性のある方法でoffset
値を足すか引くかです。
in_range
関数は以下のシグネチャを持たなければなりません。
in_range(val
type1,base
type1,offset
type2,sub
bool,less
bool) returns bool
val
とbase
は同じ型でなければならず、これは演算子族でサポートされる型の一つ(すなわち、並び順を提供する対象の型)です。
しかしながら、offset
は異なる型のものでも可能です。それは演算子族でサポートされないものでもよいです。
例としては、組み込みのtime_ops
族がinterval
型のoffset
を持つin_range
関数を提供しています。
演算子族は、任意のサポートされる型と一つまたは複数のoffset
型に対するin_range
関数を提供できます。
各in_range
関数は、pg_amproc
にtype1
と等しいamproclefttype
とtype2
に等しいamproclefttype
で登録されるべきです。
in_range
関数の本質的なセマンティクスは2つのBooleanフラグパラメータに依存します。
これは以下のように、base
にoffset
を加算または減算して、それからval
を結果と比較すべきです。
!
sub
かつ
!
less
であるなら、
val
>=
(base
+
offset
)を返します
!
sub
かつ
less
であるなら、
val
<=
(base
+
offset
)を返します
sub
かつ
!
less
であるなら、
val
>=
(base
-
offset
)を返します
sub
かつ
less
であるなら、
val
<=
(base
-
offset
)を返します
このように実行する前に、本関数は、offset
の符号を検査すべきです。
すなわち、負であったなら、エラーERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE
(22013)、エラー文面としては「invalid preceding or following size in window function(ウィンドウ関数で先行または後続のサイズが不正です)」などを出すことです。
(意味上の必要性が乏しいと見られることから非標準の演算子族はこの制限を無視することを選ぶかもしれませんが、これは標準SQLで必要とされています。)
中核コードが特定のデータ型における「ゼロより小さい」ことの意味を理解しなくても良いように、この要件はin_range
関数に委託されます。
さらに期待されることは、in_range
関数は、実用的には、base
+
offset
やbase
-
offset
がオーバーフローする場合にエラーを投げるのを避けるべきです。
たとえ値がデータ型の範囲を超えたとしても正しい比較結果は決定できます。
データ型が「infinity」や「NaN」などの概念を含む場合には、in_range
の結果が演算子族の通常のソート順序と一致するように特別な対応が必要となることに注意してください。
in_range
関数の結果は、演算子族で規定されるソート順序と整合していなければなりません。
正確には、与えらえれた任意のoffset
とsub
の修正値は以下のようになります。
less
= trueのin_range
がいくつかのval1
とbase
に対して真であるなら、同じbase
の全てのval2
<=
val1
に対して真でなければなりません。
less
= trueのin_range
が、いくつかのval1
とbase
に対して偽であるなら、同じbase
の全てのval2
>=
val1
に対して偽でなければなりません。
less
= trueのin_range
がいくつかのval
とbase1
に対して真であるなら、同じval
の全てのbase2
>=
base1
に対して真でなければなりません。
less
= trueのin_range
が一部のval
とbase1
に対して偽であるなら、同じval
の全てのbase2
<=
base1
に対して偽でなければなりません。
less
= falseのときには、逆条件の類似した命題が適用できます。
整列しようとしている型(type1
)が照合可能であるなら、標準のPG_GET_COLLATION()機構を使って、in_range
関数に適切な照合順序のOIDが渡されます。
in_range
関数は、通例STRICTと印付けされ、NULL入力を処理する必要がありません。
equalimage
省略可能ですが、btree演算子族はequalimage
(「イメージ等価を意味する等価」)サポート関数を提供してもよいです。これはサポート関数4番で登録されます。
この関数は、中核コードがbtree重複排除の最適化を適用するのが安全かを決定できるようにします。
今のところ、equalimage
関数はインデックスの構築または再構築時にのみ呼び出されます。
equalimage
関数は以下のシグネチャを持たなければなりません。
equalimage(opcintype
oid
) returns bool
戻り値は演算子クラスと照合順序についての静的な情報です。
true
を返すことは、A
およびB
引数が何らセマンティック情報を損失すること無しに交換可能でもあるとき、演算子クラスに対するorder
関数が0
(「引数が等しい」)だけを返すことが保証されていることを示します。
equalimage
関数が登録されていなかったり、false
を返すことは、この条件は守られないであろうことを示します。
opcintype
引数は演算子クラスタがインデックスを作るデータ型の
です。
これは同じ基となるpg_type
.oidequalimage
関数を演算子クラスを横断して再利用できるようになる利便性があります。
opcintype
が照合可能なデータ型である場合には、適切な照合順序のOIDが、標準のPG_GET_COLLATION()
機構を使って、equalimage
関数に渡されます。
演算子クラスに関する限り、true
を返すことは、重複排除が安全(あるいはequalimage
関数に渡されたOIDの照合順序について安全)であることを示します。
しかしながら、コアコードは、全てのインデックス列がequalimage
関数を登録する演算子クラスを使っていて、各関数が呼ばれたとき実際にtrue
を返すときに、そのインデックスに対して重複排除を安全と見做すだけです。
イメージ等価は単純にビット毎に等しいこととほとんど同じ条件です。
一点微妙な違いがあります。varlenaデータ型にインデックス作成するとき、入力時の一貫性のないTOAST圧縮の適用のために、同じdatumの二つのイメージのディスク上の表現はビット毎には等しくないかもしれません。
これまでは、演算子クラスのequalimage
関数がtrue
を返すときには、datum_image_eq()
C関数が常に演算子クラスのorder
関数と一致すると想定して安全でした(同じ照合順序のOIDがequalimage
とorder
の両関数に渡されるとして)。
コアコードは基本的に、複数データ型の族の中の演算子クラスの「等価性がイメージ等価性を含む」状態について、同族の他の演算子クラスの詳細に基づいた、いかなる推測もできません。
また、ある演算子族が型にまたがってequalimage
関数を登録していることを認識できず、そのような試みはエラーになります。
これは「等価性がイメージ等価性を含む」状態は、演算子族の階層でおおむね定義されている、ソートと等価性のセマンティクスに依存しているだけでは無いためです。
一般に、ある特定データ型の実装によるセマンティクスは別個に考慮されなければなりません。
コアPostgreSQL配布物に含まれる演算子クラスが従う慣習は、標準品、すなわち、一般的なequalimage
関数を登録することです。
大部分の演算子クラスタはbtequalimage()
を登録しています。これは重複排除が無条件に安全であることを示しています。
text
などの照合可能なデータ型に対する演算子クラスはbtvarstrequalimage()
を登録します。これは決定的な照合順序では重複排除が安全であることを示します。
サードパーティ拡張におけるベストプラクティスは制御を保つためにそれら自身のカスタム関数を登録することです。
options
省略可能ですが、B-treeの演算子族はoptions
(「演算子クラス固有オプション」)サポート関数を提供してもよいです。これはサポート関数5番に登録されます。
この関数はユーザに見える演算子クラスの振る舞いを制御するパラメータの集合を定義します。
options
サポート関数は以下のシグネチャを持たなければなりません。
options(relopts
local_relopts *
) returns void
関数にはlocal_relopts
構造体へのポインタが渡されます。ここには演算子クラス固有のオプションの集合が書かれている必要があります。
このオプションにはPG_HAS_OPCLASS_OPTIONS()
およびPG_GET_OPCLASS_OPTIONS()
マクロを使って他のサポート関数からアクセスが可能です。
今のところ、options
サポート関数を持ったB-Treeの演算子クラスはありません。
B-treeはGiST、SP-GiST、GINおよびBRINで行われているような柔軟なキーの表現を許していません。
そのため、おそらくはoptions
が現在のB-treeインデックスアクセスメソッドで多数適用されることはありません。
それでも、統一性のためにサポート関数がB-treeに追加されました。おそらくPostgreSQLでのB-treeの更なる進化の過程で使用法を見つけ出すでしょう。
本節では、上級ユーザに役立つかもしれない、B-Treeインデックスの実装の詳細について説明します。
更なる詳細、B-Tree実装の内部に焦点をあてた記述については、ソース配布物のsrc/backend/access/nbtree/README
を参照してください。
PostgreSQLのB-Treeインデックスは複数階層のツリー構造で、ツリーの各階層はページの双方向連結リストとして使用できます。 一つのメタページがインデックスの最初のセグメントファイルの固定位置に格納されます。 それ以外の全てのページはリーフページか内部ページのいずれかです。 リーフページはツリーの最下階層にあるページです。 それ以外の全ての階層は内部ページで構成されます。 各リーフページはテーブルの行を指すタプルを含みます。 各内部ページはツリーの次の下位層を指すタプルを含みます。 典型的には、全ページの99%以上がリーフページです。 内部ページとリーフページは共に、65.6に記載されている標準のページ書式を使用します。
既存リーフページがやってくるタプルをはめ込むことができないとき、新たなリーフページがB-Treeインデックスに追加されます。 ページ分割操作は一部のアイテムを新ページに動かすことで、当初は溢れているページに属していたアイテムのために空間を作ります。 ページ分割は、また、新ページへの新たなダウンリンクを親ページに挿入しなければなりません。これは親ページの分割を同様に引き起こすかもしれません。 ページは分割は再帰的に「上向きに連鎖」します。 最終的にルートページが新たなダウンリンクをはめ込みできないときには、ルートページ分割が実施されます。 これは元のルートページの一つ上の階層に新たなルートページを作ることで、ツリー構造に新しい階層を加えます。
B-Treeインデックスは、MVCCの下で同じ論理テーブル行の複数の現存するバージョンが存在する可能性があることを直接認識していません。
インデックスに対して、各タプルは独自のインデックスエントリを必要とする独立したオブジェクトです。
「バージョンチャーン」タプルは蓄積し、クエリ待ち時間とスループットに悪影響を与える可能性があります。
これは通常、個々の更新のほとんどがHOT最適化を適用できないようなUPDATE
の重いワークロードで発生します。
UPDATE
中にあるインデックスによって、カバーされる一つだけの行の値を変更するには、新しいインデックスタプルのセットをいつも必要とします。
テーブル上にある ありとあらゆるインデックスにつき一つです。
具体的には、UPDATE
によって「論理的に変更」されなかったインデックスが含まれることに注意してください。
すべてのインデックスは、テーブル上で最新バージョンを指す後継の物理的なインデックスタプルを必要とします。
各インデックス中のそれぞれの新しいタプルは通常元の「更新された」タプルと短期間共存する必要があります(通常、UPDATE
トランザクションのコミットした直後までです)。
B-Treeインデックスは、ボトムアップインデックスの削除パスの実行によって、バージョンチャーンのインデックスタプルを徐々に削除します。
各削除パスは、予期された「バージョンチャーンのページ分割」に対してトリガされます。
これは、UPDATE
文によって論理的に変更されてないインデックスだけで発生します。
さもないと、特定のページで使われなくなったバージョンが集中的に蓄積されます。
ある種の実装レベルの発見的手法は、均一のごみインデックスタプルの特定及び削除に失敗する可能がありますが、ページの分割は通常避けることができます(ページ分割もしくは重複排除パスの場合に、リーフページ上の収まらない新しいタプルが入ることの問題が解決します)。
インデックススキャンが(単一の論理行に対して)通過しなければならない最悪の場合のバージョン数は、システム全体の応答性やスループットに重要な影響があります。
ボトムアップインデックス削除パスは、論理行とバージョンを含む定性的な特徴に基づいた単一のリーフページ内の疑わしいごみタプルを対象としています。
これは、一定の定量的なテーブルレベルの閾値が超えられたとき(24.1.6参照)に起動されるautovacuumワーカーによって実行された「トップダウン」インデックスのクリーンアップと対照的です。
B-Treeインデックス内で実行されたすべての削除操作がボトムアップ削除操作とは限りません。
インデックスタプルの削除の異なる分類があります。
それは、単純なインデックスのタプル削除です。
これは、削除が安全であると分かるインデックスタプル(アイテム識別子のLP_DEAD
ビットが既に設定されているタプル)を削除する遅延メンテナンス操作です。
ボトムアップインデックス削除と同様に、単純インデックス削除は分割を回避する方法としてページ分割が予測された時点で実行されます。
単純削除は、最近のインデックススキャンでは影響があるアイテムにLP_DEAD
ビットをセットする際に、ついでに実行できる機会の場合のみに実行されるという意味で、機会主義と言えます。
PostgreSQL 14より前では、B-Treeの削除の種類は単純な削除のみでした。
単純な削除とボトムアップ削除の主な違いは、前者だけがインデックススキャンの動きによって機会を狙って駆動されることに対して、後者だけがインデックスカラムが論理的に変更されないUPDATE
からのバージョンチャーンを具体的に対象とすることです。
ボトムアップインデックス削除は、明確なワークロードによる特定インデックスのすべてのゴミインデックスタプルの掃除の大多数を実行します。
これは、インデックスがカバーするカラムを論理的に変更することが滅多にまたは決してないUPDATE
からの有意なバージョンチャーンに依存するB-Treeインデックスで予想されます。
論理行ごとのバージョン数の平均と最も悪いケースは、対象とされた増分削除パスによって純粋に低く維持することができます。
特定インデックスのディスク上のサイズは、UPDATE
からの一定のバージョンチャーンがあるにも関わらず、ページやブロックが一つも増加することがない可能性が十分にあります。
そのような場合でも、VACUUM
操作(通常、自動バキュームワーカープロセスで実行します)による、徹底的な「一掃」が、テーブルとその各インデックスの共通のクリーンアップの一部として最終的に要求されます。
VACUUM
とは異なり、ボトムアップインデックス削除は最も古いゴミのインデックスタプルがどのくらい経過しているかについて強い保証を提供しません。
テーブルと全てのインデックスの合計によって共通する保守的な切り捨て点より前に、不要になる「浮いているゴミ」インデックスタプルの維持を許可することはできません。
この基本的なテーブルレベルの不変条件は、テーブルのTIDを安全にリサイクルします。
これにより、時間の経過と共に異なる論理行が同じテーブルTIDを再利用することが可能です(ただし、これは存続期間が同じVACUUM
サイクルにまたがる、二つの論理行に同時には発生しません)。
重複とは、同じインデックスで全てのインデックスキー列が少なくとも一つの他のリーフページタプルの該当する列の値と一致する値をもっている、リーフページタプル(テーブルの行を指すタプル)です。 重複タプルは実際によくあります。 オプションの技法「重複排除」が有効にされているとき、B-Treeインデックスは、特別な重複に対する空間効率の良い表現方法を使用できます。
重複排除は重複タプルのグループを定期的に合併して、各グループに対する単一のポスティングリストタプルを形成することで機能します。 この表現方法では列のキー値は一度だけ現れます。 テーブルの行を指すTIDのソートされた配列がこれに続きます。 概して各値(あるいは列値の異なる組み合わせ)が複数回出現する場合に、これは顕著にインデックスの格納サイズを減らします。 問い合わせの遅延も顕著に削減できます。 全体的な問い合わせのスループットも顕著に増加するかもしれません。 インデックスのバキューム処理のオーバーヘッドも顕著に削減されるかもしれません。
B-Tree重複排除は、B-Tree演算子クラスの=
項に従ってNULL値が決して互いに等しくならないとしても、NULL値を含む「重複」に効果的です。
ディスク上のB-Tree構造を解するいかなる実装部分に関しても、NULLはまさにインデックス値の定義域以外の一つの値です。
既存のリーフページに収まらない新たな要素が挿入されたとき、重複排除の処理は怠惰に実行されますが、インデックスタプルの削除は新しいアイテムのための十分なスペースを解放できなかった場合に限ります(通常、削除は簡易に検討した上で無視されます)。 GINのポスティングリストのタプルと違って、B-Treeのポスティングリストのタプルは新たな重複が挿入される度に拡張する必要がありません。それらはリーフページの元の論理内容に対する単なる代替の物理表現にすぎません。 この設計は読み書き混合のワークロードでの性能の一貫性を重視しています。 ほとんどのクライアントアプリケーションは重複排除を使うことで少なくとも控えめな性能の恩恵を確認することができるでしょう。 重複排除はデフォルトで有効になっています。
CREATE INDEX
とREINDEX
は、使用する手順が若干異なりますが、ポスティングリストタプルを作って重複排除を適用します。
テーブルから取得されてソートされた入力で遭遇した重複した通常タプルの各グループは、現在のペンディングリーフページに追加される前に、ポスティングリストタプルにマージされます。
個別のポスティングリストタプルには、可能な限り多数のTIDが詰め込まれます。
リーフページは、重複排除用の別パスではなく、通常の方法で書き出されます。
この戦略はCREATE INDEX
とREINDEX
に良く適合します。これらは1回で終わるバッチ操作であるからです。
インデックスの値に重複が無いか殆ど無いために重複排除から利益を得られない、書き込みの多いワークロードには、(重複排除が明示的に無効化されて居ない限り)固定のペナルティによる小さい負荷増があります。
deduplicate_items
格納パラメータは個別のインデックス内で重複排除を無効化するのに使うことができます。
ポスティングリストタプルの読み込みは少なくとも通常タプル表現の読み込み程度に効率的であるため、読み込みのみのワークロードで性能ペナルティは一切ありません。
通常は重複排除を無効化することは有益ではありません。
一意性インデックス(や一意制約)が重複排除に使用できる場合があります。 これにより、リーフページは余分なバージョンチャーンの重複を一時的に「吸収」することができます。 一意性インデックス内の重複排除は、特に時間のかかるトランザクションがガベージコレクションを妨げるスナップショットを保持している場合にボトムアップインデックス削除を増強します。 目的は、ボトムアップインデックス削除の戦略が再び有効になるための時間を稼ぐことです。 一つの時間のかかるトランザクションが自然に消えるまでページ分割を遅らせることで、以前の削除パスが失敗した場所でボトムアップ削除パスを成功することができます。
一意性インデックスで重複排除パスを実行すべきかどうかの判断には、特別なヒューリスティックが適用されます。
これは、しばしばリーフページ分割まで連続してスキップして、無益な重複排除パスでの無駄なサイクルによる性能ペナルティを回避できます。
重複排除のオーバーヘッドを懸念するなら、選択的に設定deduplicate_items = off
を検討してください。
一意性インデックスでは重複排除を無効にすることに不都合はありません。
実装レベルの制限により、重複排除は全ての場合に使えるわけではありません。
重複排除の安全性はCREATE INDEX
あるいはREINDEX
が実行されたときに決定されます。
等しいデータの間で意味的に明らかな違いを伴う以下の場合には、重複排除は安全でないと見做されて使用できないことに注意してください。
非決定的な照合順序が使われているときtext
、varchar
、および、char
は重複排除を使えません。
等しいデータの間で大文字小文字やアクセントの違いが維持されなければなりません。
numeric
は重複排除を使えません。
等しいデータの間で数の表示スケールが維持されなければなりません。
jsonb
のB-Tree演算子クラスは内部的にnumeric
を使っているため、jsonb
は重複排除を使えません。
float4
およびfloat8
は重複排除を使えません。
これらの型は-0
と0
に異なる表現を持ち、にもかかわらずこれらは等しいと見做されます。
この違いは維持されなければなりません。
さらに以下の実装レベルの制限があります。これはPostgreSQLの将来バージョンで解消されるかもしれません。
コンテナ型(複合型、配列型、あるいは、範囲型など)は、重複排除を使えません。
さらに以下の実装レベルの制限があります。これは使われている演算子クラスや照合順序にかかわりなく該当します。
INCLUDE
インデックスには重複排除は使えません。