Contents
Building pg_turbovec from source on NixOS / nix-using systems
This is what worked on the dev machine; adapt to your environment as necessary.
1. Prerequisites
- A Rust toolchain >= 1.85.
- Nix-installed PostgreSQL 16 (we’ll copy it to a writable location).
- libclang (for
pgrx-pg-sysbindgen). - libopenblas + openblas-dev headers (for
turbovec). - pkg-config + libssl + libssl-dev (for
cargo install cargo-pgrx).
2. Build cargo-pgrx
SSLDIR=$(find /nix/store -maxdepth 3 -name 'openssl-3*-dev' -type d | head -1)
SSLDIR=${SSLDIR%-dev}
SSLDEV=${SSLDIR}-dev
OPENSSL_LIB_DIR=$SSLDIR/lib OPENSSL_INCLUDE_DIR=$SSLDEV/include OPENSSL_NO_VENDOR=1 \
cargo install cargo-pgrx --version 0.17.0
3. Set up a writable PG16 install
The system pg_config lives in a Nix store path that’s read-only;
cargo pgrx install would fail trying to copy the .control file
into it. We copy the install tree to a writable location and wrap
pg_config to rewrite its hard-coded paths:
PG_RO=$(find /nix/store -maxdepth 3 -name 'postgresql-16.9' -type d | head -1)
PG_RO_DEV=$(find /nix/store -maxdepth 3 -name 'postgresql-16.9-dev' -type d | head -1)
PG_RW=$HOME/.pgrx/install-pg16
mkdir -p $PG_RW
cp -rfL $PG_RO/. $PG_RW/. && chmod -R u+w $PG_RW
cp -rfL $PG_RO_DEV/bin/. $PG_RW/bin/. && chmod -R u+w $PG_RW/bin
mkdir -p $PG_RW/include
cp -rfL $PG_RO_DEV/include/. $PG_RW/include/. && chmod -R u+w $PG_RW/include
cp -rfL $PG_RO_DEV/lib/. $PG_RW/lib/. && chmod -R u+w $PG_RW/lib
# Wrap pg_config so its --pkglibdir / --sharedir / --bindir all
# point at the writable copy.
mv $PG_RW/bin/pg_config $PG_RW/bin/pg_config.real
cat > $PG_RW/bin/pg_config <<WRAP
#!/bin/sh
"$PG_RW/bin/pg_config.real" "\$@" \
| sed "s|$PG_RO_DEV|$PG_RW|g; s|$PG_RO|$PG_RW|g"
WRAP
chmod +x $PG_RW/bin/pg_config
Then point pgrx at it:
mkdir -p $HOME/.pgrx
cat > $HOME/.pgrx/config.toml <<EOF
[configs]
pg16 = "$PG_RW/bin/pg_config"
EOF
4. Initialise the per-version data directory
cargo pgrx init --pg16 $PG_RW/bin/pg_config
5. Build & test
cd ~/ws/pg_turbovec
export LIBCLANG_PATH=$(find /nix/store -maxdepth 3 -name 'clang-*-lib' -type d | head -1)/lib
GLIBC_INC=$(find /nix/store -maxdepth 3 -name 'glibc-*-dev' -type d | head -1)/include
CLANG_INC=$LIBCLANG_PATH/clang/$(ls $LIBCLANG_PATH/clang | head -1)/include
export BINDGEN_EXTRA_CLANG_ARGS="-isystem $GLIBC_INC -isystem $CLANG_INC"
export RUSTFLAGS="-L $(find /nix/store -maxdepth 3 -name 'openblas-0.3.30' -type d | head -1)/lib"
# Default-feature build (type, ops, knn(), aggregates, casts).
cargo build --no-default-features --features pg16
cargo pgrx test pg16
# Stripped-down build (no index AM, no .so footprint for the AM):
cargo build --no-default-features --features pg16
(As of v1.3.0 / Phase Q the experimental_index_am and
relfile_storage Cargo features have been retired. The index
AM and the relfile-resident storage path are default-on; there
is no opt-in flag.)
6. Verified outcomes
default features (pg16 + index AM): 109 passed; 0 failed
7. Why so much manual setup?
Because cargo pgrx init download requires ICU + a writable
extension dir + ~10 minutes to compile Postgres from source, and
the local machine already has a working PG16 install. The wrapper
around pg_config is the cleanest way to redirect the read-only
Nix store paths to a writable copy without rebuilding anything.
For a non-Nix system (Ubuntu, Fedora, macOS Homebrew) none of this gymnastics is needed:
cargo install cargo-pgrx --version 0.17.0
cargo pgrx init # uses system pg_config
cargo pgrx test pg16