pub enum IndexKind {
BTree(PersistentBTreeMap<IndexKey, PostingList>),
Nsw(NswGraph),
Brin {
column_type: DataType,
summaries: Vec<Option<(i64, i64)>>,
},
Gin(PersistentBTreeMap<String, PostingList>),
GinTrgm(PersistentBTreeMap<String, PostingList>),
GinFulltext(PersistentBTreeMap<String, PostingList>),
GinJsonb(PersistentBTreeMap<String, PostingList>),
BTreeMulti(PersistentBTreeMap<Box<[IndexKey]>, PostingList>),
}Variants§
BTree(PersistentBTreeMap<IndexKey, PostingList>)
v4.40: structural-sharing B-tree over IndexKey. Replaces the v0.8
BTreeMap<IndexKey, Vec<usize>> — Index::clone is now an Arc
bump regardless of index size, so Catalog::clone inside the
v4.34 auto-commit wrap stays O(1) even for tables with secondary
indices (the case that bottlenecked v4.39 at 1M rows in the
sweep).
v5.1: value type widened from Vec<usize> to Vec<RowLocator> so
a single key can point to a mix of hot-tier rows (RowLocator::Hot,
equivalent to the pre-v5 usize row index) and cold-tier rows
(RowLocator::Cold { segment_id, page_offset }) once the v5.2
freezer starts producing them. Pre-v5.2 only Hot entries appear
— the on-disk encoding stays at FILE_VERSION 8 (raw u64 row index)
because every locator round-trips through RowLocator::from_legacy_v8_u64
without information loss. FILE_VERSION 9 with tagged encoding lands
alongside the first freezer commit (v5.1 step 2b / v5.2).
Nsw(NswGraph)
Navigable-small-world graph for vector kNN search.
Brin
v6.7.1 — BRIN (Block Range INdex). Pure metadata: BRIN
indexes carry NO in-memory key→locator map. The (min,
max) summaries live in each cold-tier segment’s v2
envelope sidecar; the BRIN entry in Table.indices only
records THAT a BRIN index exists on this column so the
segment encoder + planner can opt into the summary path.
Fields
column_type: DataTypeThe cell type at column_position at CREATE INDEX time.
Used by the planner to type-check WHERE-clause range
predicates against the BRIN-indexed column.
summaries: Vec<Option<(i64, i64)>>v7.38.11 — one (min, max) per BRIN_RANGE_ROWS slots of
the hot tier, so a range predicate can skip the ranges that
cannot contain a match.
Maintenance is WIDEN-ONLY and that is the whole safety argument: an insert widens its range, an update widens, and a delete leaves the range alone. A range left wider than the rows it now covers is correct and merely less selective — which is exactly PG’s contract for a lossy index, since the predicate is re-checked on every row the summary lets through. A summary may over-report; it can never under-report, so no matching row can be skipped.
None for a range whose rows carry no comparable key (all
NULL, say), and such a range is never skipped.
Gin(PersistentBTreeMap<String, PostingList>)
v7.12.3 — GIN inverted index over a tsvector column.
Storage shape: lexeme word → Vec<RowLocator>. The posting
list per word is appended in row-order, so range scans are
O(matching rows) once the per-word lookup is done. Multi-
term queries intersect / union posting lists.
IndexKey::from_value(TsVector) returns None — GIN doesn’t
participate in try_index_seek (which is BTree-equality-keyed).
The engine consults this index through try_gin_lookup on
WHERE col @@ tsquery predicates instead.
Backed by a PersistentBTreeMap so Catalog::clone (the
per-write snapshot) stays O(1) — same structural-sharing
invariant as BTree.
GinTrgm(PersistentBTreeMap<String, PostingList>)
v7.15.0 — USING gin (col gin_trgm_ops) over a TEXT
column. Posting lists map trigram (PG-compatible 3-byte
shingle on the lower-cased + space-padded input) to row
locators. The planner uses this index to accelerate
WHERE col LIKE '…' / ILIKE '…' / similarity(col, q) > t — every literal run of length ≥ 1 in the pattern
produces a trigram set, the engine intersects the posting
lists, and the LIKE / similarity predicate is re-evaluated
per candidate row to filter the over-approximation.
Persisted via tag-4 index payload in FILE_VERSION 24+.
GinFulltext(PersistentBTreeMap<String, PostingList>)
v7.17.0 Phase 2.2 — MySQL FULLTEXT KEY (col) over a
TEXT / VARCHAR column. Posting lists map
tsvector('simple') lexeme to row locators. At insert /
build time the engine derives the lexemes from the cell
via the same lower-case tokenisation rule as
to_tsvector('simple', ...) — the column itself stays a
plain text type on disk (mysqldump round-trips would be
broken otherwise). The planner uses this index to
accelerate MySQL-shape MATCH(col) AGAINST('term')
queries by mapping them onto the existing tsquery @@
walker. Persisted via tag-5 index payload in
FILE_VERSION 33+.
GinJsonb(PersistentBTreeMap<String, PostingList>)
v7.37.8(sentori Epic 5 P2)— USING gin (col) over a
JSON / JSONB column. Posting lists map a canonical
(path, leaf) token(see crate::jsonb_gin::extract_tokens)
to row locators so the planner can resolve
<col> @> <jsonb_literal> to a candidate row set via
posting-list intersection + per-row json::contains
re-verification. Pre-7.37.8 the same DDL loaded as a
BTree fallback so pg_dump JSONB-GIN scripts kept loading
without query-time acceleration. Persisted via tag-6 index
payload in FILE_VERSION 51+.
BTreeMulti(PersistentBTreeMap<Box<[IndexKey]>, PostingList>)
v7.38.1 (L12) — a REAL multi-column B-tree: the key is the whole
column tuple, [leading, extras…], ordered lexicographically by
slice Ord. That ordering is the entire design: every key
sharing a prefix is contiguous, so an equality on a PREFIX of
the columns is one O(log N) descent plus a bounded walk, and a
full-tuple equality is a point get. The single-column BTree
kind used to stand in for multi-column DDL by keying on the
leading column only and carrying the rest as metadata — TPC-C’s
customer (c_w_id, c_d_id, c_last, c_first) then answered a
three-column equality with every row of one warehouse and a
per-row filter over 30 000 candidates.
Rows where any component column is NULL (or of an unkeyable
type) are NOT entered: this index serves = probes, and in SQL
col = v never selects a NULL. Uniqueness keeps its own
full-tuple walk with NULLS-DISTINCT semantics on the
enforcement path, exactly as before.
Persisted via tag-7 index payload in FILE_VERSION 91+.
Implementations§
Source§impl IndexKind
impl IndexKind
Sourcepub fn approx_resident_bytes(&self) -> u64
pub fn approx_resident_bytes(&self) -> u64
v7.31 (memory campaign, C2) — bytes this index variant holds
resident in RAM, computed by walking its OWN structure rather
than a parametric guess made by the engine. Replaces the old
spg_admin::memory_stats inline match, which charged NSW with
a stale m_max_0 * 8 per node (neighbour slots are u32 = 4 B
since v6.1.x, and most nodes never fill m_max_0) and lumped
every GIN family index into a flat 1 KiB token — a gross
undercount for the text-heavy posting lists that dominate
mailrs’ footprint. Per-entry container overhead uses the
3-word (24 B on 64-bit) Vec/String header as the charge.
O(index entries): operator/monitoring surface (memory_stats /
spg_memory_stats), not a query path.