Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion nix/checks.nix
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
}:
let
pg_prove = pkgs.perlPackages.TAPParserSourceHandlerpgTAP;
inherit (self'.packages) pg_regress;
inherit (self'.packages) pg_regress pg_isolation_regress;
getkey-script = pkgs.stdenv.mkDerivation {
name = "pgsodium-getkey";
buildCommand = ''
Expand Down Expand Up @@ -273,6 +273,11 @@
testList;

sortedTestList = builtins.sort (a: b: a < b) filteredTestList;

# Concurrency/isolation specs run via pg_isolation_regress (the stock
# PostgreSQL isolation tester). Specs live in tests/isolation/specs/,
# expected output in tests/isolation/expected/. Add new spec names here.
isolationSpecList = [ "sample_isolation" ];
in
pkgs.writeShellApplication rec {
name = "postgres-${pgpkg.version}-check-harness";
Expand All @@ -287,6 +292,7 @@
pgpkg
pg_prove
pg_regress
pg_isolation_regress
procps
start-postgres-server-bin
which
Expand Down Expand Up @@ -556,6 +562,37 @@
fi
log info "pg_regress tests completed successfully"

# Isolation (concurrency) tests via pg_isolation_regress -- the stock
# PostgreSQL isolation tester shipped in the build. Reuses the same
# running server as pg_regress (--use-existing). These specs target
# core/heap + contrib concurrency behaviour. Skipped on:
# - CLI variants: portable build runs only a test subset.
# - orioledb ships its own isolation suite for its storage-engine
# concurrency semantics.
#shellcheck disable=SC2193
if ${lib.boolToString isCliVariant}; then
log info "CLI variant detected - skipping isolation tests"
elif [[ "${pgpkg.version}" == *"_"* ]]; then
log info "orioledb variant detected - skipping isolation tests (orioledb has its own isolation suite)"
else
log info "Running pg_isolation_regress tests (${builtins.toString (builtins.length isolationSpecList)} specs)"
mkdir -p "$out/isolation_output"
if ! log_cmd pg_isolation_regress \
--use-existing \
--dbname=postgres \
--inputdir=${./tests/isolation} \
--outputdir="$out/isolation_output" \
--host=localhost \
--port=${pgPort} \
--user=supabase_admin \
${builtins.concatStringsSep " " isolationSpecList} 2>&1; then
log error "pg_isolation_regress tests failed"
cat "$out/isolation_output/output_iso/regression.diffs" 2>/dev/null || cat "$out/isolation_output/regression.diffs" 2>/dev/null || true
exit 1
fi
log info "pg_isolation_regress tests completed successfully"
fi

# Skip migrations tests for CLI variants (they may depend on extensions)
if ${lib.boolToString isCliVariant}; then
log info "CLI variant detected - skipping migrations tests"
Expand Down Expand Up @@ -890,6 +927,7 @@
goss
image-size-analyzer
pg_regress
pg_isolation_regress
pg-startup-profiler
supabase-cli
supascan
Expand Down
29 changes: 29 additions & 0 deletions nix/ext/pg_isolation_regress.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
lib,
stdenv,
postgresql,
}:

stdenv.mkDerivation {
pname = "pg_isolation_regress";
version = postgresql.version;

phases = [ "installPhase" ];

# pg_isolation_regress and its helper isolationtester are built as part of the
# standard PostgreSQL source tree (src/test/isolation) and ship in the pgxs
# tree. pg_isolation_regress locates isolationtester relative to its own
# binary, so both must live in the same directory.
installPhase = ''
mkdir -p $out/bin
cp ${postgresql}/lib/pgxs/src/test/isolation/pg_isolation_regress $out/bin/
cp ${postgresql}/lib/pgxs/src/test/isolation/isolationtester $out/bin/
'';

meta = with lib; {
description = "Concurrent-isolation regression testing tool for PostgreSQL";
homepage = "https://www.postgresql.org/";
platforms = postgresql.meta.platforms;
license = licenses.postgresql;
};
}
8 changes: 8 additions & 0 deletions nix/packages/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
postgresqlPackage = self'.packages."postgresql_${version}";
in
pkgs.callPackage ../ext/pg_regress.nix { postgresql = postgresqlPackage; };
# Function to create the pg_isolation_regress package
makePgIsolationRegress =
version:
let
postgresqlPackage = self'.packages."postgresql_${version}";
in
pkgs.callPackage ../ext/pg_isolation_regress.nix { postgresql = postgresqlPackage; };
pgsqlSuperuser = "supabase_admin";
supascan-pkgs = pkgs.callPackage ./supascan.nix {
inherit (pkgs) lib;
Expand Down Expand Up @@ -67,6 +74,7 @@
pg-restore = pkgs.callPackage ./pg-restore.nix { psql_15 = self'.packages."psql_15/bin"; };
pg_prove = pkgs.perlPackages.TAPParserSourceHandlerpgTAP;
pg_regress = makePgRegress activeVersion;
pg_isolation_regress = makePgIsolationRegress activeVersion;
run-testinfra = pkgs.callPackage ./run-testinfra.nix { };
show-commands = pkgs.callPackage ./show-commands.nix { };
start-client = pkgs.callPackage ./start-client.nix {
Expand Down
20 changes: 20 additions & 0 deletions nix/tests/isolation/expected/sample_isolation.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Parsed test spec with 2 sessions

starting permutation: s1_begin s1_update s2_begin s2_read s1_commit s2_read s2_commit
step s1_begin: BEGIN;
step s1_update: UPDATE iso_sample SET val = val + 1 WHERE id = 1;
step s2_begin: BEGIN ISOLATION LEVEL READ COMMITTED;
step s2_read: SELECT val FROM iso_sample WHERE id = 1;
val
---
100
(1 row)

step s1_commit: COMMIT;
step s2_read: SELECT val FROM iso_sample WHERE id = 1;
val
---
101
(1 row)

step s2_commit: COMMIT;
31 changes: 31 additions & 0 deletions nix/tests/isolation/specs/sample_isolation.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Sample isolation spec -- proves the pg_isolation_regress harness runs in CI.
# This is not a regression test for any particular fix; it just exercises the
# stock PostgreSQL isolation tester so real concurrency specs (e.g. MERGE
# serialization, postgres_fdw EvalPlanQual) can be dropped in alongside it.
#
# Scenario: session s2 reads a row under READ COMMITTED before and after a
# concurrent UPDATE+COMMIT by session s1 -- the second read observes the
# committed change. No step blocks.

setup
{
CREATE TABLE iso_sample (id int PRIMARY KEY, val int);
INSERT INTO iso_sample VALUES (1, 100);
}

teardown
{
DROP TABLE iso_sample;
}

session s1
step s1_begin { BEGIN; }
step s1_update { UPDATE iso_sample SET val = val + 1 WHERE id = 1; }
step s1_commit { COMMIT; }

session s2
step s2_begin { BEGIN ISOLATION LEVEL READ COMMITTED; }
step s2_read { SELECT val FROM iso_sample WHERE id = 1; }
step s2_commit { COMMIT; }

permutation s1_begin s1_update s2_begin s2_read s1_commit s2_read s2_commit
Loading