pub enum IndexKey {
Int(i64),
Text(String),
Bool(bool),
Uuid([u8; 16]),
Bytes(Vec<u8>),
Numeric(Box<NumericKey>),
Null,
}Expand description
Key type accepted by secondary indices. Float / NULL / Vector values
can’t participate in a B-tree index — f64 is only PartialOrd, NULL
has SQL-three-valued semantics, and Vector belongs to the (future) HNSW
path. Index lookups on those columns fall back to full scan.
Variants§
Int(i64)
Text(String)
Bool(bool)
Uuid([u8; 16])
v7.17.0 — Value::Uuid index key. Comparison is byte-wise
(RFC 4122 byte order) so PRIMARY KEY UUID lookups land on
the same fast-path as Int / Text.
Bytes(Vec<u8>)
r1039 — Value::Bytes (bytea). PG orders bytea by plain byte
comparison, shorter-prefix first ('' < \x00 < \x0000 < \x01ff < \xff, measured on 18.4), which is exactly Vec<u8>’s Ord.
Numeric(Box<NumericKey>)
r1039 — exact decimal, in the canonical form described on
NumericKey.
r1040 — BOXED, and the box is load-bearing for every OTHER index.
A NumericKey is 48 bytes against Text(String)’s 24, so inline
it set the size of the whole enum and every B-tree node in every
index grew with it: 32 bytes per key to 48, align 8 to 16.
Measured through the release sweep, SELECT pad FROM t ORDER BY id over 400,000 rows — a walk of the primary key’s index — went
39.4-40.6 ms to 42.3-44.1, in both leg orders. The indirection is
charged to numeric keys, which are new, instead of to every index
that existed already.
Null
v7.38.1 (L12) — a NULL component INSIDE a composite key, and
nothing else. IndexKey::from_value(Value::Null) still returns
None, so single-column B-trees never hold one, and no probe
path ever BUILDS one (col = NULL is not a match in SQL) — the
variant is only reachable through a composite key’s component
list, where it exists so that a row like (2, 3, NULL) stays
findable by a PREFIX probe on (w, d). Declared last: slice
Ord then sorts NULL components after every value, PG’s
NULLS LAST.
Implementations§
Source§impl IndexKey
impl IndexKey
Sourcepub fn from_i64(n: i64) -> Self
pub fn from_i64(n: i64) -> Self
v7.37.43 (INSUBQ B-4) — inline-friendly BigInt fast path.
try_count_star_pk_in_subquery_fast (and any other hot loop
probing an integer PK) already holds an i64; this builds the
IndexKey without going through the generic from_value
dispatch tree.
Sourcepub fn from_value_for_column(v: &Value<'_>, ty: DataType) -> Option<Self>
pub fn from_value_for_column(v: &Value<'_>, ty: DataType) -> Option<Self>
r1039 — the key a value takes when the INDEXED COLUMN is ty, or
None when it takes none (→ the caller falls back to a scan).
Every key under one index comes from one column, so they all live in one key SPACE. A probe built in a different space finds nothing — and “nothing” is indistinguishable from “no matching rows”, which is how round 564 and r1037 both turned an index into a wrong answer (a TEXT key sought against a DATE-keyed and a UUID-keyed index).
The two spaces this round adds make that trap reachable again from
a new direction: WHERE n = 2 on a NUMERIC column produces
Value::Int, and an integer key would look in a space nothing
lives in. So NUMERIC columns take integers by converting them
exactly, and refuse anything they cannot convert; BYTEA columns
take only Value::Bytes; and no other column may be keyed in
either of the two new spaces.
Use this wherever the key comes from a LITERAL or from another
table’s value. IndexKey::from_value stays right for building
the index itself, where the value is the column’s own.