# Test harness environment reference

> Environment variables and defaults for run-regression, serial/pg_regress, isolation, auth/ldap TAP, and recovery TAP: PGRUST_BIN, PGRUST_PSQL, PG_SRC, PG_INSTALL, ports, OUTDIR.

- Repository: malisper/pgrust
- GitHub: https://github.com/malisper/pgrust
- Human docs: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21
- Complete Markdown: https://grok-wiki.com/public/docs/malisper-pgrust-7e0c8e82bc21/llms-full.txt

## Source Files

- `scripts/run-regression`
- `scripts/run-pg-regress-serial`
- `scripts/run-pg-isolation`
- `scripts/run-auth-tap`
- `scripts/run-recovery-tap`
- `scripts/run-ldap-tap`

---

---
title: "Test harness environment reference"
description: "Environment variables and defaults for run-regression, serial/pg_regress, isolation, auth/ldap TAP, and recovery TAP: PGRUST_BIN, PGRUST_PSQL, PG_SRC, PG_INSTALL, ports, OUTDIR."
---

`scripts/run-regression`, `scripts/run-pg-regress-serial` (via `scripts/run-pg-regress`), `scripts/run-pg-isolation`, `scripts/run-auth-tap`, `scripts/run-ldap-tap`, and `scripts/run-recovery-tap` read a small set of environment variables to locate the pgrust `postgres` binary, a PostgreSQL 18 client or C install, SQL/expected sources, ports, and output directories. Clone-only regression uses vendored `vendor/postgres-18.3/` and needs only a `psql` client; serial, isolation, and TAP harnesses require an external Postgres 18.3 source tree and/or C install.

## Harness matrix

| Script | Role | Needs `PG_SRC` | Needs `PG_INSTALL` | Default `PGPORT` | Default `OUTDIR` pattern |
| --- | --- | --- | --- | --- | --- |
| `scripts/run-regression` | Clone-only main regress | No (vendored) | No | `55434` | `$TMPDIR/pgrust_regress.XXXXXX` |
| `scripts/run-pg-regress` | Thin wrapper → serial | Yes (required) | Yes (required) | (via serial) | (via serial) |
| `scripts/run-pg-regress-serial` | Serial SQL vs C tools | Yes (required) | Yes (required) | `55433` | `$TMPDIR/pgrust_pg_regress_serial.XXXXXX` |
| `scripts/run-pg-isolation` | Isolation schedule | Yes (required) | Yes (required) | `55434` | `$TMPDIR/pgrust_pg_isolation.XXXXXX` |
| `scripts/run-auth-tap` | Auth TAP via `prove` | Yes (defaulted) | Yes (defaulted) | (Cluster ports) | fixed `TESTDATADIR` |
| `scripts/run-ldap-tap` | LDAP TAP via `prove` | Yes (defaulted) | Yes (defaulted) | (Cluster ports) | fixed `TESTDATADIR` |
| `scripts/run-recovery-tap` | Recovery TAP via `prove` | Yes (defaulted) | Yes (defaulted) | (Cluster ports) | fixed `TESTDATADIR` |

`$TMPDIR` falls back to `/tmp` when unset. Tempdir names use `mktemp -d`.

## Shared binary and path variables

<ParamField body="PGRUST_BIN" type="path" default="&lt;repo&gt;/target/release/postgres">
Path to the pgrust `postgres` executable. Every harness checks `-x` and exits with a build hint if missing: `cargo build --release --bin postgres`.
</ParamField>

<ParamField body="PGRUST_PSQL" type="path" optional>
**`run-regression` only.** Explicit `psql` client. Resolution order when unset: `command -v psql`, then `/tmp/pgrust_pginstall/bin/psql`. Exit if none is executable.
</ParamField>

<ParamField body="PG_SRC" type="path">
PostgreSQL **18.3 source tree**.

- **Required** for `run-pg-regress-serial` / `run-pg-regress` and `run-pg-isolation` (no default; script exits if unset).
- **Defaulted** for TAP scripts: `$repo_root/../../pgrust/postgres-18.3`, then fallback `$HOME/workspace/work/pgrust/postgres-18.3` if the suite directory is missing under the first path.

Suite probes: `src/test/regress` (serial), `src/test/isolation` (isolation), `src/test/authentication` / `ldap` / `recovery` (TAP).
</ParamField>

<ParamField body="PG_INSTALL" type="path">
Full C PostgreSQL 18.3 install prefix (`bin/`, `lib/`, `share/`).

- **Required** for serial and isolation (must provide `bin/initdb`, `bin/psql`; serial also needs `bin/createdb`; isolation needs `initdb`/`psql` only).
- **Default** for TAP: `/tmp/pgrust_pginstall`. TAP requires executable `bin/initdb` and directory `share/postgresql`.
</ParamField>

<ParamField body="PG_BUILD" type="path" default="PG_SRC">
**`run-pg-isolation` only.** Build tree that may contain `src/test/isolation/pg_isolation_regress`.
</ParamField>

<ParamField body="PG_ISOLATION_REGRESS" type="path" optional>
**`run-pg-isolation` only.** Explicit path to `pg_isolation_regress`. If unset, searches `$PG_BUILD/src/test/isolation/pg_isolation_regress` then `$PG_SRC/src/test/isolation/pg_isolation_regress`.
</ParamField>

## Ports, output, and debug stops

| Variable | Harnesses | Default | Notes |
| --- | --- | --- | --- |
| `PGPORT` | regression, serial, isolation | `55434` (regression, isolation); `55433` (serial) | Unix socket only (`listen_addresses=` empty). TAP clusters choose their own ports via PostgreSQL::Test::Cluster. |
| `OUTDIR` | regression, serial, isolation | `mktemp` under `$TMPDIR` (see matrix) | Holds `data/`, `postmaster.log`, `initdb.log`, per-test `*.out`, `_status.tsv` (SQL runners), `results/` (SQL runners). Isolation also uses `$OUTDIR/output_iso`. |
| `STOP_AFTER` | regression, serial | unset | When set to a test name, stop after that file (debugging). |
| `TMPDIR` | regression, serial, isolation | `/tmp` | Base for default `OUTDIR`. |

### Layout under `OUTDIR` (SQL runners)

```text
$OUTDIR/
  data/              # PGDATA from initdb / --initdb
  postmaster.log
  initdb.log
  results/           # server-side COPY / lo_export targets
  _status.tsv        # name, status, diff-lines, best expected
  _order.txt         # run order
  <test>.out         # actual psql output
  sock/              # serial/isolation socket dir (under OUTDIR)
```

`run-regression` places the Unix socket in a **short** path `/tmp/pgr.XXXXXX` (not under `OUTDIR`) because of the macOS `sun_path` length limit. Serial and isolation keep the socket at `$OUTDIR/sock`.

## Client, locale, and stack environment (SQL harnesses)

Exported by `run-regression`, `run-pg-regress-serial`, and `run-pg-isolation` (unless already set):

| Variable | Default | Purpose |
| --- | --- | --- |
| `PGTZ` | `America/Los_Angeles` | Match Postgres regress expected timestamps |
| `PGDATESTYLE` | `Postgres, MDY` | Date output style |
| `LANG` | `C` | Locale for messages |
| `LC_MESSAGES` | `C` | Message catalog locale |
| `PGOPTIONS` | `-c intervalstyle=postgres_verbose` | Session GUC for interval formatting |
| `RUST_MIN_STACK` | `33554432` (32 MiB) | Rust thread stack for deep recursion |

Also: `unset LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC LC_TIME LANGUAGE` so partial locale env does not skew diffs.

`run-regression` / serial additionally set:

- `PG_ABS_SRCDIR` → regress directory (`vendor/.../regress` or `$PG_SRC/src/test/regress`)
- serial: `PG_LIBDIR=$PG_INSTALL/lib/postgresql`, `PG_DLSUFFIX` (`.dylib` on Darwin, else `.so` if unset)

Postmaster command line (SQL harnesses): `-c io_method=sync -c max_stack_depth=60000`, plus `ulimit -s 65520` when allowed. Checkpoint/autovacuum/replication worker GUCs are fixed in-script, not env-driven.

## `run-regression` (clone-only)

**Prerequisites:** release `postgres`, vendored tree `vendor/postgres-18.3/` (`share/postgres.bki`, `regress/sql`, `regress/expected`, `regress/parallel_schedule`), PostgreSQL 18 `psql`.

**Not used:** `PG_SRC`, `PG_INSTALL`.

**Init:** `"$PGRUST_BIN" --initdb -D "$DATADIR" -L "$SHARE" --no-locale --encoding UTF8 -U postgres` with `SHARE=$repo_root/vendor/postgres-18.3/share`.

**psql vars:** `abs_srcdir=$REGRESS`, `abs_builddir=$OUTDIR`, `libdir='$libdir'`, `dlsuffix=.so` (regress helpers resolve as builtin `regress`, not a real `.so` path).

**Args:** no args → all names from `parallel_schedule` `test:` lines; args → those tests with `test_setup` auto-prepended if missing.

**Status values:** `PASS`, `FAIL`, `PSQL_FAIL`, `CRASHED` in `_status.tsv`. Exit non-zero if any non-`PASS`.

<RequestExample>
```bash
# full suite
scripts/run-regression

# subset + keep output
OUTDIR=/tmp/my-regress PGPORT=55434 \
  PGRUST_BIN="$PWD/target/release/postgres" \
  PGRUST_PSQL="$(brew --prefix libpq)/bin/psql" \
  scripts/run-regression select numeric

# stop after one test
STOP_AFTER=select scripts/run-regression select insert
```
</RequestExample>

## Serial `pg_regress` (`run-pg-regress` / `run-pg-regress-serial`)

`run-pg-regress` execs `run-pg-regress-serial`.

**Required:** `PG_SRC`, `PG_INSTALL` (with `initdb`, `psql`, `createdb`).

**Init:** C `"$PG_INSTALL/bin/initdb"` (not pgrust `--initdb`). Server is still `$PGRUST_BIN`. Client tools are always from `$PG_INSTALL/bin`.

**Default port `55433`** (differs from clone-only / isolation `55434`).

**libdir:** `-v libdir=$PG_INSTALL/lib/postgresql` (C extension path model; not the clone-only `$libdir` token).

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/tmp/pgrust_pginstall
scripts/run-pg-regress-serial boolean int4
# or
scripts/run-pg-regress
```

## Isolation (`run-pg-isolation`)

**Required:** `PG_SRC`, `PG_INSTALL`, and a built `pg_isolation_regress` (via `PG_ISOLATION_REGRESS` or `PG_BUILD`/`PG_SRC` search).

**Init:** C `initdb`; server `$PGRUST_BIN`; then execs `pg_isolation_regress --use-existing` with host=`$SOCK`, port=`$PGPORT` (default `55434`), `--bindir=$PG_INSTALL/bin`, schedule `$PG_SRC/src/test/isolation/isolation_schedule`, output `$OUTDIR/output_iso`.

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/tmp/pgrust_pginstall
export PG_BUILD=/path/to/postgres-18.3/build   # if isolation tool is only there
scripts/run-pg-isolation
```

## TAP harnesses (auth / ldap / recovery)

All three: assemble a **fake install** under a fixed path, put real copies of C `pg_ctl` and pgrust `postgres` in `bin/` (so `realpath` does not escape to C postgres), symlink remaining tools to `$PG_INSTALL/bin/*`, set `PATH`/`TEMP_CONFIG`/`PG_REGRESS`/`TESTDATADIR`, then `exec prove -v`.

### TAP-shared environment

| Variable | Default | Role |
| --- | --- | --- |
| `PGRUST_BIN` | `<repo>/target/release/postgres` | Copied to fake `bin/postgres` |
| `PG_SRC` | sibling path then `$HOME/workspace/work/pgrust/postgres-18.3` | Suite + `src/test/perl` on `PERL5LIB` |
| `PG_INSTALL` | `/tmp/pgrust_pginstall` | C tools + share/lib |
| `PG_TEST_TIMEOUT_DEFAULT` | `90` | Cluster poll timeout (seconds) |
| `TEMP_CONFIG` | harness-written conf under `/tmp` | Forces `io_method = sync` and `max_stack_depth = 7000kB` |
| `PG_REGRESS` | `$PG_SRC/src/test/regress/pg_regress` | Cluster auth config helper |
| `TESTDATADIR` | harness-fixed under `/tmp` | Cluster data root |
| `PERL5LIB` | `$PG_SRC/src/test/perl` (+ optional local `IPC::Run`) | TAP modules |
| `DYLD_LIBRARY_PATH` / `LD_LIBRARY_PATH` | prepend `$PG_INSTALL/lib` | C client libs |

### Fixed install / data paths

| Harness | Fake install (`INST`) | `TEMP_CONFIG` file | `TESTDATADIR` |
| --- | --- | --- | --- |
| recovery | `/tmp/pgrust_recovery_tap_inst` | `/tmp/pgrust_recovery_tap_tempconfig.conf` | `/tmp/pgrust_recovery_tap_data` |
| auth | `/tmp/pgrust_auth_tap_inst` | `/tmp/pgrust_auth_tap_tempconfig.conf` | `/tmp/pgrust_auth_tap_data` |
| ldap | `/tmp/pgrust_ldap_tap_inst` | `/tmp/pgrust_ldap_tap_tempconfig.conf` | `/tmp/pgrust_ldap_tap_data` |

Each run `rm -rf`s its `INST` and `TESTDATADIR` (and local `tmp_check` / `log` under the suite directory) before recreate.

### Build-capability env (skip gates)

Unset configure flags would fatal-fail under TAP `use warnings FATAL => 'all'`. Scripts default:

| Variable | recovery / auth default | ldap default |
| --- | --- | --- |
| `with_icu` | `no` | `no` |
| `with_ssl` | `openssl_no` (any non-`openssl` skips SSL) | `openssl_no` |
| `with_gssapi` | `no` | `no` |
| `with_ldap` | `no` | `yes` |
| `with_pam` | `no` | `yes` |
| `with_readline` | `no` | `no` |
| `enable_injection_points` | `no` (auth only) | — |
| `PG_TEST_EXTRA` | — | `ldap` (enables slapd suite) |

### Default test files when argv empty

| Harness | Default tests |
| --- | --- |
| `run-recovery-tap` | `t/002_archiving.pl` `t/003_recovery_targets.pl` `t/020_archive_status.pl` `t/023_pitr_prepared_xact.pl` `t/024_archive_recovery.pl` `t/025_stuck_on_old_timeline.pl` `t/042_low_level_backup.pl` `t/045_archive_restartpoint.pl` |
| `run-auth-tap` | `t/003_peer.pl` `t/004_file_inclusion.pl` `t/006_login_trigger.pl` `t/007_pre_auth.pl` |
| `run-ldap-tap` | `t/001_auth.pl` |

### TAP self-check

Before `prove`, each TAP script resolves `postgres` the way `pg_ctl` does and requires the binary to contain the string `PGRUST_TRACE`. Failure prints `FATAL: pg_ctl would launch a NON-pgrust postgres` and exits — the historical bug was symlinking `pg_ctl` so `realpath` landed on C postgres.

```text
  $INST/bin/pg_ctl      = real copy of C pg_ctl   (realpath stays in INST)
  $INST/bin/postgres    = real copy of PGRUST_BIN
  $INST/bin/*           = symlinks → $PG_INSTALL/bin/*  (initdb/psql escape to C)
  $INST/share,lib       = symlinks → $PG_INSTALL
```

Server under test is pgrust; bootstrap (`initdb`) and clients stay on C tools.

```bash
export PG_SRC=/path/to/postgres-18.3
export PG_INSTALL=/tmp/pgrust_pginstall
export PGRUST_BIN="$PWD/target/release/postgres"
export PG_TEST_TIMEOUT_DEFAULT=120
scripts/run-recovery-tap t/002_archiving.pl
scripts/run-auth-tap
PG_TEST_EXTRA=ldap scripts/run-ldap-tap
```

## Server settings injected by harnesses

Not environment variables, but required for successful boots:

| Setting | SQL harnesses (CLI) | TAP (`TEMP_CONFIG`) |
| --- | --- | --- |
| `io_method` | `sync` | `sync` |
| `max_stack_depth` | `60000` (GUC units as passed) | `7000kB` |
| `listen_addresses` | empty (socket only) | (Cluster defaults) |
| `checkpoint_timeout` | `3600` | — |
| `autovacuum` | `on` | — |
| `max_logical_replication_workers` | `0` | — |

Without `io_method=sync`, pgrust panics at startup (worker IO unported).

## Failure modes

| Symptom | Likely cause / env |
| --- | --- |
| `pgrust postgres not found at ...` | Build missing or wrong `PGRUST_BIN` |
| `no psql client found` | Clone-only: set `PGRUST_PSQL` or install libpq client |
| `set PG_SRC` / `set PG_INSTALL` | Serial/isolation: exports required, no defaults |
| `C postgres source tree not found` | TAP: set `PG_SRC` to a tree with the suite dir |
| `full C install not found at PG_INSTALL=...` | TAP: need `bin/initdb` + `share/postgresql` |
| `pg_isolation_regress not found` | Build isolation tests or set `PG_ISOLATION_REGRESS` / `PG_BUILD` |
| `FATAL: pg_ctl would launch a NON-pgrust postgres` | Fake-install layout broken; do not replace real `pg_ctl`/`postgres` copies with pure symlinks |
| `postmaster did not start` | Check `$OUTDIR/postmaster.log` (often missing `io_method=sync` outside harness) |
| False TAP FAIL on skips | Capability env unset; harness defaults `with_*` / `enable_injection_points` intentionally |
| Socket connect failures on macOS | Clone-only already uses short `/tmp/pgr.*` socket dir; avoid long custom socket paths |

## Quick env cheat sheet

```bash
# Universal
PGRUST_BIN=$PWD/target/release/postgres

# Clone-only
PGRUST_PSQL=...   PGPORT=55434   OUTDIR=...   STOP_AFTER=...

# Serial / isolation
PG_SRC=...   PG_INSTALL=...   PGPORT=55433|55434   OUTDIR=...
PG_BUILD=...   PG_ISOLATION_REGRESS=...   # isolation only
STOP_AFTER=...   # serial only

# TAP
PG_SRC=...   PG_INSTALL=/tmp/pgrust_pginstall
PG_TEST_TIMEOUT_DEFAULT=90
PG_TEST_EXTRA=ldap            # ldap suite
# optional overrides of with_icu / with_ssl / with_ldap / enable_injection_points
```

## Related pages

<CardGroup>
  <Card title="Run regression tests" href="/regression-tests">
    Clone-only workflow, selective tests, and optional isolation/auth/recovery harness usage.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Runtime GUCs harnesses force: `io_method`, stack depth, and build-time path env vars.
  </Card>
  <Card title="Install paths and share directory" href="/install-paths">
    Share tree and `-L` wiring used by clone-only `--initdb`.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Missing `psql`, stack overflows, and `io_method=sync` failures outside harnesses.
  </Card>
  <Card title="Build from source" href="/build-from-source">
    Produce `target/release/postgres` expected by `PGRUST_BIN` defaults.
  </Card>
  <Card title="Compatibility and status" href="/compatibility">
    Regression-oracle model and non-production limits that shape these suites.
  </Card>
</CardGroup>
