Skip to content

Latest commit

 

History

History
246 lines (202 loc) · 10.9 KB

File metadata and controls

246 lines (202 loc) · 10.9 KB

Supported definitions and compatibility

This is the release contract for pgvector_hypo 0.1.0. It describes what the extension models, what it deliberately rejects, and which claims are measured against physical pgvector indexes.

Platform boundary

Component Supported contract
PostgreSQL Major version 16
pgvector 0.8.x semantics, tested at 0.8.0 and 0.8.5
Index access method IVFFlat only
Extension behavior Session-local, top-level plain EXPLAIN planning only

PostgreSQL 17 and newer are not implied by this table. A new pgvector or PostgreSQL version must rerun the compatibility workflow and public-data gate before it can extend the claim.

Type and opclass matrix

Fixed result type Official opclass Ordering operator Valid dimensions Default
vector(n) vector_l2_ops <-> 1–2,000 yes
vector(n) vector_ip_ops <#> 2–2,000 no
vector(n) vector_cosine_ops <=> 2–2,000 no
halfvec(n) halfvec_l2_ops <-> 1–4,000 no
halfvec(n) halfvec_ip_ops <#> 2–4,000 no
halfvec(n) halfvec_cosine_ops <=> 2–4,000 no
bit(n) bit_hamming_ops <~> 1–64,000 no

The inner-product and cosine minima come from pgvector's spherical-k-means normalization support procedure, not from an opclass-name exception. Cosine page estimation excludes NULLs and sampled values whose official normalization procedure cannot produce a positive norm. Bit has no norm exclusion.

Explicit and default opclasses resolve through PostgreSQL namespaces and search paths. The resolved type, access method, opclass, opfamily, and support procedures must all be the official objects owned by the installed vector extension. A same-named custom opclass is rejected.

Definition-form matrix

Every row in the type/opclass table supports the following forms:

Form Contract
Ordinary key One fixed-dimension column; redundant parentheses around a bare column remain an ordinary key.
Expression key One transformed expression that PostgreSQL proves immutable after planning and whose result has the exact fixed type/typmod required by the opclass.
Partial index One PostgreSQL-valid immutable predicate. Normal predicate implication determines eligibility.
Partial expression Structural expression matching and predicate implication are independent gates; both must pass.
Partitioned parent Key and predicate trees map from the registered parent directly to compatible leaves, including reordered physical columns.
Generated column Treated as an ordinary fixed-dimension column.
Materialized view Ordinary and expression definitions use the same rules as a table.

binary_quantize(embedding)::bit(n) with bit_hamming_ops and embedding::halfvec(n) with a halfvec opclass are documented expression examples, not hard-coded expression patterns. PostgreSQL matches prepared expression trees rather than SQL text.

The pages column returned by pgvector_hypo_list() and pgvector_hypo_list_indexes is bigint. This preserves the full unsigned BlockNumber range used by PostgreSQL relation metadata.

An expression index has no physical expression-statistics object at registration time. Its row/page estimate therefore treats every relation row as indexable before applying a partial predicate. Ordinary keys can use column NULL, norm, and dominant-most-common-value evidence. Missing or incomplete statistics always fall back to the conservative policy.

Registrations keep catalog OIDs and transformed trees in backend-local memory. Renames preserve compatible OID/attribute identity. Type or typmod changes, drop/recreate, detach, extension relocation after registration, and missing functions/operators make an old registration fail closed; a new registration can resolve the new catalog state.

The registering role must own the target relation, matching physical CREATE INDEX. When TABLESPACE is omitted, registration captures PostgreSQL's current default index tablespace and its permission check; later GUC changes do not alter that definition. A dropped captured tablespace and partition-parent lock contention both withhold candidates until the definition can be modeled safely.

Deliberately unsupported

The following are rejected or withheld rather than modeled speculatively:

  • HNSW and access methods other than IVFFlat;
  • sparsevec, variable-dimension vector/halfvec, and bit varying keys;
  • custom opclasses, opclass options, and unsupported type/opclass pairings;
  • multicolumn and included-column indexes;
  • per-key collation, ordering, or NULL-order modifiers;
  • an explicit TABLESPACE clause, CONCURRENTLY, IF NOT EXISTS, and ON ONLY;
  • mutable expressions or predicates that remain mutable after PostgreSQL planning;
  • executable use, including ordinary execution, EXPLAIN ANALYZE, and EXPLAIN EXECUTE;
  • PostgreSQL majors other than 16.

A caller-provided index name is syntactically accepted, but the session registration receives a checked fake OID and a generated hypothetical display name. No catalog index or relation file is created.

Evidence and acceptance

The release gate compares physical and hypothetical definitions, pages, startup/total costs, selected scan family, and selected index identity. It requires:

  • zero safety failures and definition-parity failures;
  • zero canonical planner-choice mismatches;
  • median relative page/startup/total-cost error at most 10%;
  • p95 relative page/startup/total-cost error at most 20%;
  • the maximum always published rather than averaged away;
  • evidence on both sides of pgvector's TOAST startup-cost branch.

Relative error is abs(hypothetical - physical) / physical. A small numeric difference that preserves the chosen path is a fidelity measurement. A planner-choice mismatch is more serious because it changes the advice presented to the user. Scan-shape, index-identity, rejected-candidate cost, fuzzy-cost ties, pathkey ties, and physical page variance remain separately named diagnostics.

Current public-data result

The authoritative seven-opclass run was generated on 2026-07-26 at 05:26 UTC with PostgreSQL 16.14, pgvector 0.8.5, pgvector_hypo 0.1.0, 100,000 source rows per public dataset, and three physical builds per page observation. The median-page build supplied the canonical physical plans.

make evidencecheck \
  EVIDENCE_DSN=postgresql:///pgvector_hypo_release_final_20260726 \
  EVIDENCE_PYTHON=/tmp/pgvector-hypo-evidence-venv/bin/python \
  EVIDENCE_ROWS=100000 \
  EVIDENCE_OUTPUT_DIR=artifacts/evidence/issue-14-release-final-source-20260726 \
  EVIDENCE_ARGS='--physical-page-samples=3'
Result Observation
Strict overall gate FAIL
Baseline / plan / page cases 118 / 2,032 / 27
Planner-choice mismatches 2
Safety failures 0
Page error p50 / p95 / max 0.11% / 0.97% / 3.01%
Startup-cost error p50 / p95 / max 0.00% / 0.85% / 2.04%
Total-cost error p50 / p95 / max 0.00% / 0.60% / 2.04%
TOAST adjusted / non-adjusted cases 2 / 4
Official opclasses observed 7 / 7

Both reported mismatches are the same saturated-probe boundary, repeated at probes=400 and 401 because both clamp to a probe ratio of one: GloVe-derived bit(100), lists=400, tenant_1pct, LIMIT 10. The median physical build had 632 pages and selected IVFFlat; the hypothetical model estimated 651 pages and selected a sequential scan. Physical samples were [632, 626, 635], and only two of the three physical builds chose IVFFlat for that query.

This is a measured k-means/build-variance planner boundary, not a definition or safety mismatch. It is also not waived: results.json retains passed=false because the strict gate continues to count every canonical planner-choice mismatch. A fixed-bit-specific headroom change was rejected after it made an independent five-family bit matrix worse. The known limitation is that standard column statistics cannot predict the exact k-means page phase closely enough to guarantee a stable decision at this boundary.

The artifact directory is artifacts/evidence/issue-14-release-final-source-20260726:

File SHA-256
manifest.json 94c23e0dfc72ae43121b7e4a06621402b090a4b673b324b115c8e1071066bd6e
results.json 58e42e5f7307cea0edaeec6fbeb2c60e8e5dfc2ef2770e01f8f114bd6ec02f1b
summary.md 9d400f9897f65bd4d827fe4d397f5872b33836f7070d70427db45a0219e7d4dc

The source dataset checksums recorded in the manifest are:

Source SHA-256
SIFT dd6f0a6ed6b7ebb8934680f861a33ed01ff33991eaee4fd60914d854a0ca5984
GloVe 544af1d5e84e112cd4749571dcfd8ca109818a572f850af75a3a09e093a953c4
Last.fm 2bb2886857b01fabf939fcb7478717cd9ecf03b3fa66f0f0bef325f510ce38fd

The public-data adapter reuses the same source rows:

Source Derived supported families
ANN Benchmarks SIFT vector and halfvec L2
ANN Benchmarks Last.fm vector and halfvec inner product
ANN Benchmarks GloVe vector and halfvec cosine; deterministic binary-quantized bit/Hamming

The generated CI smoke uses deterministic data and downloads nothing. It is a bounded orchestration/compatibility gate, not a replacement for the 100,000-row public-data release run.

Reproduce from a release tag

Start with an immutable tagged archive, not a dirty checkout:

tag=v0.1.0
git archive --format=tar.gz --prefix=pgvector_hypo-0.1.0/ \
  -o pgvector_hypo-0.1.0.tar.gz "$tag"
shasum -a 256 pgvector_hypo-0.1.0.tar.gz
tar -xzf pgvector_hypo-0.1.0.tar.gz
cd pgvector_hypo-0.1.0

With PostgreSQL 16 and one declared pgvector version installed for the same pg_config:

make
make install
make installcheck
make pycheck APP_PYTHON=/path/to/evidence-venv/bin/python
make appcheck \
  APP_DSN=postgresql:///pgvector_hypo_evidence \
  APP_PYTHON=/path/to/evidence-venv/bin/python
make evidenceintegration \
  EVIDENCE_DSN=postgresql:///pgvector_hypo_evidence \
  EVIDENCE_PYTHON=/path/to/evidence-venv/bin/python
make evidencesmoke \
  EVIDENCE_DSN=postgresql:///pgvector_hypo_evidence \
  EVIDENCE_PYTHON=/path/to/evidence-venv/bin/python
make evidencecheck \
  EVIDENCE_DSN=postgresql:///pgvector_hypo_evidence \
  EVIDENCE_PYTHON=/path/to/evidence-venv/bin/python \
  EVIDENCE_ROWS=100000 \
  EVIDENCE_ARGS='--physical-page-samples=3' \
  EVIDENCE_OUTPUT_DIR=artifacts/evidence/release-0.1.0

Run the build/regression/application/generated gates with both pgvector 0.8.0 and 0.8.5. The public release-fidelity run is pinned to pgvector 0.8.5, whose mirrored IVFFlat implementation is the source baseline. Preserve manifest.json, results.json, and summary.md together; their canonical facts are validated before publication.

Mirroring upstream page layout and cost code creates a maintenance obligation. When pgvector changes its type support procedures, dimension limits, tuple/list layout, or IVFFlat cost callback, update the storage policy and model only after rerunning definition parity and both evidence adapters.