github pgcentralfoundation/pgrx v0.20.0-beta.0

pre-release42 minutes ago

Welcome to pgrx v0.20.0-beta.0, the first beta of v0.20.0. This is a big one.

pgrx now generates Rust versions of Postgres' C macros and static inline functions during every build, and the hand-written ports are gone. We also fixed a long list of safe APIs that could cause undefined behavior, dropped Postgres 13 and 14, moved to Postgres 19rc1, and now require Rust 1.99.1. cargo-pgrx can assemble upgrade scripts from SQL fragments and build RPM and DEB packages.

Many of these changes will break existing code, some of them in behavior rather than at compile time. Please read the v0.20.0 Migration Guide before upgrading.

Install with:

$ rustup update stable
$ cargo install cargo-pgrx --version 0.20.0-beta.0 --locked

And update your extension's dependencies with cargo pgrx upgrade --to =0.20.0-beta.0. Your existing cargo pgrx init configuration keeps working. To trade a downloaded Postgres 19 beta for 19rc1, run cargo pgrx init --pg19=download.

Builds now need a clang whose major version matches libclang, and should have llvm-ar from the same LLVM release. On Debian-likes that's apt install clang llvm libclang-dev. See Build requirements.

This is a beta. Please try it with your extensions and tell us what breaks before 0.20.0 ships.


Generated C Macros and Static Inline Functions

This is the headline item, and it arrived on develop through #2405.

pgrx has always carried hand-written Rust ports of the Postgres macros and static inline functions it needed. They covered a small part of the headers, and they drifted from the C as Postgres changed from version to version.

The new pgrx-c-macros crate runs during every pgrx-pg-sys build and translates Postgres' function-like C macros into documented Rust macro_rules!. The translations keep C's arithmetic, integer promotions, casts and evaluation order, so they compute what the C computes for the headers you build against. You call them by their Postgres names and extract the result at the Rust boundary:

// Before (v0.19)
let len = pgrx::varsize_any(ptr);
check_for_interrupts!();

// After (v0.20)
let len = unsafe { pgrx::VARSIZE_ANY!(ptr).get() as usize };
pgrx::check_for_interrupts();

A macro that can't be translated faithfully is skipped, and OUT_DIR/pgNN_macro_report.json says why.

Static inline functions get the same treatment. When the translator supports every statement in a function's body, the body becomes Rust, and pg_sys::NAME(...) runs that translation with the binding's ordinary signature. Otherwise pg_sys::NAME is still the guarded C binding. On PG16 and later, that means hot conversions such as Int32GetDatum no longer pay for a sigsetjmp and a thread check on every call.

The hand-written ports are removed, including pgrx::varsize_any, check_for_interrupts!, pg_sys::heap_tuple_get_struct and the pgrx::srf_* helpers. The C macro guide lists each one with its replacement. A generated macro that reads storage or backend state needs an unsafe block, even where the helper it replaces was safe.

Follow-up PRs made the generated macros nicer to use from Rust:

In short: .get_usize() and .get_as::<T>() replace most .get() as ... casts, pg_sys::Oid::from(...) and friends convert macro results, and pg_sys::maxalign and the rest of pg_sys::align work in const contexts. pg_sys::expression_tree_walker and the other walkers and mutators no longer need a cfg split between PG15 and PG16+. pg_sys::InvalidOid and the transaction ID constants are still Oid and TransactionId, so code written for 0.19 that uses them compiles unchanged.

Fixes along the way

#2422 fixes builds with clang 21 and glibc 2.43, as on Ubuntu 26.04, where TIMESTAMP_NOT_FINITE! and related items were missing. #2447 fixes the Windows build after #2441. The PG18-on-Windows fix is in #2415, below.


Soundness Fixes

Several pgrx APIs let safe code read freed, misaligned or mistyped Postgres memory. Fixing them changed signatures and behavior. The migration guide has a section for each, but the highlights are:

  • Copies instead of borrows. PgVarlena<T> now holds its value in Rust. A PgBox<T> read from an array element or tuple attribute is a copy in CurrentMemoryContext. PgTupleDesc returns FormData_pg_attribute by value and no longer dereferences to PgBox<TupleDescData>. PgRelation::name() and namespace() return String, and PgRelation no longer dereferences to RelationData.
  • New unsafe fns. AnyArray::into, heap_getattr, heap_getattr_datum_ex, composite_row_type_make_tuple, DatumWithTypeInfo::into_value, and the <type>_recv function generated for #[pg_binary_protocol] types depend on their input being right, so they are now unsafe. The new AnyArray::value::<T>() and AnyElement::value::<T>() check the type and return a Result.
  • Borrowed PostgresType values. A #[derive(PostgresType)] type with a lifetime now borrows from the Datum it was read from. Vec<Part<'_>> arguments no longer compile; use Array<'_, Part<'_>>.
  • fn_call checks argument types against the called function's parameters and returns FnCallError::IncompatibleArgumentType instead of crashing the backend. FnCallError is now #[non_exhaustive].
  • rust_regtypein finds an extension's own types through pg_depend instead of search_path, so an extension's own type named Point no longer resolves to pg_catalog.point.
  • PgVarlena::new requires T: Default. Use the unsafe PgVarlena::new_zeroed where all-zero bytes are a valid T.
  • Text array elements are checked. Reading text array elements as &str or String now checks their encoding as FromDatum for &str does, and panics instead of producing an invalid str.

Postgres Versions

pgrx now supports Postgres 15 through 19. Remove the pg13 and pg14 features from your Cargo.toml and change default if it names one of them. New extensions default to pg15.

  • Fix building for PostgreSQL 19 on Windows (MSVC) by @mstaros in #2388
  • Type the PG19 BUFFER_LOCK_* re-exports as BufferLockMode::Type by @eeeebbbbrrrr in #2410
  • Check PG19's LockBuffer and index lock constants in every build by @eeeebbbbrrrr in #2412

#2388 fixes building for PG19 on Windows with MSVC. The BUFFER_LOCK_* constants have the type LockBuffer takes, which is u32 on Linux and macOS and i32 on MSVC. Code that builds for several versions should pass them as pg_sys::BUFFER_LOCK_SHARE as _.

Rust 1.99.1

Not 1.99.0, which can miscompile an extern "C" function that returns bool on x86_64 (rust-lang/rust#163911). pgrx hands Postgres plenty of those, such as GUC check hooks.


cargo-pgrx

SQL Migration Fragments

  • Add cargo pgrx migrate for SQL migration fragments by @eeeebbbbrrrr in #2407
  • Make SQL migration fragments opt-in, fix release assembly, and document them by @eeeebbbbrrrr in #2406
  • Warn that temporary fragment versions differ from the crate version by @eeeebbbbrrrr in #2431

An extension can now keep its upgrade SQL as one small file per change in sql/unreleased/ instead of one hand-edited script per release. That helps when several pull requests change the schema at once, or when fixes get backported. Opt in from Cargo.toml:

[package.metadata.pgrx]
unreleased-sql-dir = "sql/unreleased"

cargo pgrx install, run, test, regress and bench then assemble the fragments into a temporary upgrade script so ALTER EXTENSION ... UPDATE works during development. cargo pgrx migrate assemble writes the real versioned script at release time, and cargo pgrx migrate check lints fragments for dependency cycles and undeclared dependencies. Extensions that don't opt in see no change.

This started as @stuhood's work in #2380. The new Upgrading Extensions chapter of the book and the migration_fragments example show how to use it.

RPM and DEB Packages

On Linux, cargo pgrx package now builds .rpm and .deb files itself. Add an empty [package.metadata.rpm] or [package.metadata.deb] section to Cargo.toml and you get a package laid out like the PGDG ones, filled in from your crate's name, version, description, license and authors. Any key you set in the section overrides the default. --no-distrib-packages skips them when you only want the staged files.

If you already have a [package.metadata.deb] section for cargo deb, it uses different keys and can now make cargo pgrx package fail. See Existing cargo deb metadata.

cargo pgrx regress

A regression test can now say which Postgres versions it runs on with a comment line such as -- pgrx-regress-skip pg15 pg16 or -- pgrx-regress-only pg17 pg18. On other versions it's reported as skipped and needs no expected output.

cargo pgrx regress also takes several test names now, and exits with status 1 when a name matches no test. Only a file named exactly setup.sql is the setup file, so a test like post_setup.sql now runs as an ordinary test.

Other cargo-pgrx Changes

  • Fix cargo pgrx upgrade reading the wrong Cargo index cache directory by @isdaniel in #2398

cargo pgrx upgrade without --to failed on Cargo 1.85 and newer. It works again.

  • feat: accept any pgrx patch release within cargo-pgrx's minor series by @Vinnu124 in #2419
  • Let a prerelease cargo-pgrx accept matching pgrx prereleases by @eeeebbbbrrrr in #2443

cargo-pgrx 0.20.x accepts any pgrx 0.20.x, so you no longer need to upgrade the CLI for every pgrx patch release. A prerelease cargo-pgrx accepts the same and later prereleases, as well as the stable release.


New Features

  • Add LWLocks for dynamic shared memory and parallel foreign scans by @eeeebbbbrrrr in #2409

pgrx::lwlock::dsm::DsmLwLockTranche and pgrx::lwlock::scan::ParallelScanLwLock provide LWLocks that live in dynamic shared memory, which a parallel foreign scan needs and PgLwLock can't provide. The new parallel_scan_lwlock example implements a parallel FDW with them. This is @fkomauli's work from #2395.

#[pg_test] tests can now open their own connections back to Postgres through pgrx_tests::postgres, which makes multi-connection tests possible.

Bugfixes

Any aggregate that implemented Aggregate::deserial failed CREATE AGGREGATE, so no pgrx aggregate with an internal state could run in parallel. deserial now takes the serialized bytes and returns Self::State.

  • Treat a not-null NULL pointer as uninitialized in Internal by @eeeebbbbrrrr in #2414
  • Document liveness and aliasing requirements for Internal's unsafe methods by @eeeebbbbrrrr in #2416

Dependencies

  • Replace petgraph with lightweight in-tree directed graph by @cbandy in #2403

With #2411, petgraph and its Zlib-licensed foldhash dependency are gone from every pgrx extension's dependency tree. Note that the generated schema can now list statements in a different order. If an extension_sql! block names an object from elsewhere in your extension, make sure it lists that object in requires, or CREATE EXTENSION can fail. See Order of the generated schema.

Documentation

Project Administrativa

New Contributors

Thanks also to @stuhood and @fkomauli, whose work became the SQL fragment and DSM LWLock features, and to @daamien, @isdaniel and @cbandy. Your work is appreciated!

Full Changelog: v0.19.3...v0.20.0-beta.0

Don't miss a new pgrx release

NewReleases is sending notifications on new releases.