Contents
PostgreSQL version support matrix
pg_turbovec supports PostgreSQL 13, 14, 15, 16, 17, 18 (and
19beta1, experimentally). CI builds the latest patch of each major on
every push (cargo pgrx init --download), so the “Tested patch” column below
is the floor at the time of writing, not a pin.
The test count (445 passed / 8 ignored, uniform across every native leg as of v2.10.2) is the same on all majors — the per-major notes below record only the C-ABI shape differences the extension shims, not test-count differences.
| Version | Status | Tests | Notes |
|---|---|---|---|
| 13.x | ✅ Supported | 445 passed / 8 ignored | aminsert 7-arg shape; no amsummarizing / amadjustmembers fields. |
| 14.x | ✅ Supported | 445 passed / 8 ignored | aminsert gained indexUnchanged; no amsummarizing field. |
| 15.x | ✅ Supported | 445 passed / 8 ignored | Same shape as 14. |
| 16.x | ✅ Supported | 445 passed / 8 ignored | Reference platform during development. |
| 17.x | ✅ Supported | 445 passed / 8 ignored | Benchmark platform. |
| 18.x | ✅ Supported | 445 passed / 8 ignored | relopt_parse_elt gained isset_offset. CI-validated every push (.github/workflows/test.yml matrix leg test (18)). |
| 19.0beta1 | ✅ Experimental (beta PG) | CI matrix leg | pgrx 0.19.1 floor. PG19 deltas handled: LockBuffer’s mode param became BufferLockMode::Type (u32) — shimmed via relfile::lock_buffer_mode; rd_locator.relNumber same as pg16+. PG19 is upstream BETA — re-validate at RC/GA before production use. |
Test counts and patch versions above are the exact numbers CI installs and reports as of the most recent green run (
gh run viewon.github/workflows/test.yml’stestjob, one leg perpg<N>matrix entry).cargo pgrx init --pgN downloadalways fetches the latest point release for majorNat run time, so these patch versions drift upward on their own — re-runbash scripts/drift-check.shand check the latest CI log rather than trusting this table blindly. The one ignored test (src/index/ivf.rs,ivf_batch_speedup) is a perf-only timing comparison, not a correctness gate; it’s#[ignore]d deliberately on every PG version, not a skip specific to any one of them.The out-of-core IVF build (v1.12.0+) uses PG’s
BufFiletemp-file API, whose signatures differ across majors (BufFileReadExactis PG16+;BufFileWrite’s pointer type changed). v1.15.1 addedscripts/compile-matrix.sh(acargo checkacross everypgNNfeature, wired into the pre-push hook) so version-specific C-API breaks are caught before tagging — a v1.12.0–v1.15.0 regression that broke the pg13/14/15/18 build legs slipped through because local dev was pg16-only.
How tests are run
As of v1.3.0 (Phase Q), the experimental_index_am and
relfile_storage Cargo features are gone; the only build knob
is pg<N>:
cargo pgrx test pg<N> --no-default-features \
--features "pg<N> pg_test"
(Or simply cargo pgrx test pg<N> if you’re happy with the
default feature set, which already enables pg16.)
For each supported version, the test suite drives every type
(vector, halfvec, sparsevec, bitvec), every distance
operator (<->, <#>, <=>, <+>, <~>, <%>), the index
access method opclasses, and aminsert / ambulkdelete via VACUUM.
Why each gate exists
(*routine).amsummarizing — cfg = pg16+
The IndexAmRoutine struct gained an amsummarizing: bool field
in PG 16 to drive BRIN’s summarising-index codepath. Earlier
versions don’t know about it.
#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18"))]
{
(*routine).amsummarizing = false;
}
(*routine).amadjustmembers — cfg = pg14+
amadjustmembers is the op-family-adjust-members callback added
in PG 14 (be08e10b41fd). pg13 doesn’t have the field.
#[cfg(not(feature = "pg13"))]
{
(*routine).amadjustmembers = None;
}
aminsert callback — split for pg13
PG 14 added an indexUnchanged: bool parameter to aminsert for
HOT-chain elision (9dc718bdf2b1). pg13’s signature is one
argument shorter. We expose two thin C-ABI wrappers selecting on
the feature flag and a shared aminsert_impl Rust function:
#[cfg(not(feature = "pg13"))]
#[pgrx::pg_guard]
pub(crate) unsafe extern "C-unwind" fn aminsert(
index_relation: pg_sys::Relation,
values: *mut pg_sys::Datum,
isnull: *mut bool,
heap_tid: pg_sys::ItemPointer,
_heap_relation: pg_sys::Relation,
_check_unique: pg_sys::IndexUniqueCheck::Type,
_index_unchanged: bool, // <-- added in PG 14
_index_info: *mut pg_sys::IndexInfo,
) -> bool {
aminsert_impl(index_relation, values, isnull, heap_tid)
}
relopt_parse_elt::isset_offset — cfg = pg18
PG 18 added isset_offset: i32 to relopt_parse_elt so callers
can distinguish “explicitly set to default” from “never set”. We
don’t track that distinction; -1 (“unused”) works on every
field.
pg_sys::relopt_parse_elt {
optname: c"bit_width".as_ptr(),
opttype: pg_sys::relopt_type::RELOPT_TYPE_INT,
offset: std::mem::offset_of!(TurbovecRelopts, bit_width) as i32,
#[cfg(feature = "pg18")]
isset_offset: -1,
},
Gotcha: pgrx::pg_guard reserves <fn>_inner
The #[pgrx::pg_guard] macro expands to a wrapper plus a private
helper named <original_name>_inner. If you split a callback
into a public C-ABI wrapper and an inner Rust impl, don’t name
the inner helper <fn>_inner — it collides with the macro’s
generated symbol. We use <fn>_impl instead. Surface that any
new callback you split follows the same convention.
Adding a future PG version
- Add
pgN = ["pgrx/pgN", "pgrx-tests/pgN"]to[features]inCargo.toml. - Run
cargo pgrx test pgN. - Compile errors will point at any new fields in
IndexAmRoutine, callback shape changes, or relopt struct drift. Add#[cfg(feature = "pgN")](or#[cfg(not(feature = "pgM"))]for “all versions <= M”) gates tosrc/index/. - Re-run the full matrix to make sure no previous version was broken.
- Update this file’s table and the
CHANGELOG.mdentry.
PostgreSQL 19
Not supported yet — PG19 is still in upstream beta (REL_19_BETA1)
and the pinned pgrx = "=0.17.0" dependency has no pg19 feature.
for the blocker detail, the C-API delta
found so far, and the recommended timeline (wait for PG19 RC1+, then
treat the pgrx upgrade + port as its own dedicated piece of work).