Skip to content

Commit c06be51

Browse files
committed
feat: support multiple versions of the supautils extension
1 parent 7f6e525 commit c06be51

File tree

5 files changed

+267
-19
lines changed

5 files changed

+267
-19
lines changed

nix/ext/supautils.nix

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,97 @@
11
{
2+
pkgs,
23
lib,
34
stdenv,
45
fetchFromGitHub,
56
postgresql,
67
}:
7-
8-
stdenv.mkDerivation rec {
8+
let
99
pname = "supautils";
10-
version = "3.0.1";
1110

12-
buildInputs = [ postgresql ];
11+
# Load version configuration from external file
12+
allVersions = (builtins.fromJSON (builtins.readFile ./versions.json)).${pname};
1313

14-
src = fetchFromGitHub {
15-
owner = "supabase";
16-
repo = pname;
17-
rev = "refs/tags/v${version}";
18-
hash = "sha256-j0iASDzmcZRLbHaS9ZNRWwzii7mcC+8wYHM0/mOLkbs=";
19-
};
14+
# Filter versions compatible with current PostgreSQL version
15+
supportedVersions = lib.filterAttrs (
16+
_: value: builtins.elem (lib.versions.major postgresql.version) value.postgresql
17+
) allVersions;
18+
19+
# Derived version information
20+
versions = lib.naturalSort (lib.attrNames supportedVersions);
21+
latestVersion = lib.last versions;
22+
numberOfVersions = builtins.length versions;
23+
packages = builtins.attrValues (
24+
lib.mapAttrs (name: value: build name value.hash) supportedVersions
25+
);
26+
27+
# Build function for individual versions
28+
build =
29+
version: hash:
30+
stdenv.mkDerivation rec {
31+
inherit pname version;
32+
33+
buildInputs = [ postgresql ];
34+
35+
src = fetchFromGitHub {
36+
owner = "supabase";
37+
repo = pname;
38+
rev = "refs/tags/v${version}";
39+
inherit hash;
40+
};
41+
42+
installPhase = ''
43+
runHook preInstall
44+
45+
mkdir -p $out/{lib,share/postgresql/extension}
46+
47+
# Install shared library with version suffix
48+
mv ${pname}${postgresql.dlSuffix} $out/lib/${pname}-${version}${postgresql.dlSuffix}
49+
50+
# Create version-specific control file
51+
cat <<EOF > $out/share/postgresql/extension/${pname}--${version}.control
52+
module_pathname = '$libdir/supautils'
53+
relocatable = false
54+
EOF
55+
56+
runHook postInstall
57+
'';
2058

21-
installPhase = ''
22-
mkdir -p $out/lib
59+
meta = with lib; {
60+
description = "PostgreSQL extension for enhanced security";
61+
homepage = "https://github.com/supabase/${pname}";
62+
maintainers = with maintainers; [ steve-chavez ];
63+
platforms = postgresql.meta.platforms;
64+
license = licenses.postgresql;
65+
};
66+
};
67+
in
68+
pkgs.buildEnv {
69+
name = pname;
70+
paths = packages;
71+
pathsToLink = [
72+
"/lib"
73+
"/share/postgresql/extension"
74+
];
75+
postBuild = ''
76+
# Create symlinks to latest version for library and control file
77+
ln -sfn ${pname}-${latestVersion}${postgresql.dlSuffix} $out/lib/${pname}${postgresql.dlSuffix}
2378
24-
install -D *${postgresql.dlSuffix} -t $out/lib
79+
# Create default control file pointing to latest
80+
{
81+
echo "default_version = '${latestVersion}'"
82+
cat $out/share/postgresql/extension/${pname}--${latestVersion}.control
83+
} > $out/share/postgresql/extension/${pname}.control
2584
'';
2685

27-
meta = with lib; {
28-
description = "PostgreSQL extension for enhanced security";
29-
homepage = "https://github.com/supabase/${pname}";
30-
maintainers = with maintainers; [ steve-chavez ];
31-
platforms = postgresql.meta.platforms;
32-
license = licenses.postgresql;
86+
passthru = {
87+
inherit versions numberOfVersions;
88+
pname = "${pname}-all";
89+
defaultSettings = {
90+
session_preload_libraries = "supautils";
91+
"supautils.disable_program" = "true";
92+
"supautils.privileged_role" = "privileged_role";
93+
};
94+
version =
95+
"multi-" + lib.concatStringsSep "-" (map (v: lib.replaceStrings [ "." ] [ "-" ] v) versions);
3396
};
3497
}

nix/ext/tests/supautils.nix

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
{ self, pkgs }:
2+
let
3+
pname = "supautils";
4+
inherit (pkgs) lib;
5+
installedExtension =
6+
postgresMajorVersion: self.packages.${pkgs.system}."psql_${postgresMajorVersion}/exts/${pname}-all";
7+
versions = postgresqlMajorVersion: (installedExtension postgresqlMajorVersion).versions;
8+
postgresqlWithExtension =
9+
postgresql:
10+
let
11+
majorVersion = lib.versions.major postgresql.version;
12+
pkg = pkgs.buildEnv {
13+
name = "postgresql-${majorVersion}-${pname}";
14+
paths = [
15+
postgresql
16+
postgresql.lib
17+
(installedExtension majorVersion)
18+
];
19+
passthru = {
20+
inherit (postgresql) version psqlSchema;
21+
lib = pkg;
22+
withPackages = _: pkg;
23+
};
24+
nativeBuildInputs = [ pkgs.makeWrapper ];
25+
pathsToLink = [
26+
"/"
27+
"/bin"
28+
"/lib"
29+
];
30+
postBuild = ''
31+
wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
32+
wrapProgram $out/bin/pg_ctl --set NIX_PGLIBDIR $out/lib
33+
wrapProgram $out/bin/pg_upgrade --set NIX_PGLIBDIR $out/lib
34+
'';
35+
};
36+
in
37+
pkg;
38+
psql_15 = postgresqlWithExtension self.packages.${pkgs.system}.postgresql_15;
39+
psql_17 = postgresqlWithExtension self.packages.${pkgs.system}.postgresql_17;
40+
in
41+
self.inputs.nixpkgs.lib.nixos.runTest {
42+
name = "supautils";
43+
hostPkgs = pkgs;
44+
nodes.server =
45+
{ config, ... }:
46+
{
47+
services.postgresql = {
48+
enable = true;
49+
package = (postgresqlWithExtension psql_15);
50+
authentication = ''
51+
local all postgres peer map=postgres
52+
local all all peer map=root
53+
'';
54+
identMap = ''
55+
root root supabase_admin
56+
postgres postgres postgres
57+
'';
58+
ensureUsers = [
59+
{
60+
name = "supabase_admin";
61+
ensureClauses.superuser = true;
62+
}
63+
];
64+
settings = (installedExtension "15").defaultSettings or { };
65+
};
66+
67+
specialisation.postgresql17.configuration = {
68+
services.postgresql = {
69+
package = lib.mkForce psql_17;
70+
};
71+
72+
systemd.services.postgresql-migrate = {
73+
serviceConfig = {
74+
Type = "oneshot";
75+
RemainAfterExit = true;
76+
User = "postgres";
77+
Group = "postgres";
78+
StateDirectory = "postgresql";
79+
WorkingDirectory = "${builtins.dirOf config.services.postgresql.dataDir}";
80+
};
81+
script =
82+
let
83+
oldPostgresql = psql_15;
84+
newPostgresql = psql_17;
85+
oldDataDir = "${builtins.dirOf config.services.postgresql.dataDir}/${oldPostgresql.psqlSchema}";
86+
newDataDir = "${builtins.dirOf config.services.postgresql.dataDir}/${newPostgresql.psqlSchema}";
87+
in
88+
''
89+
if [[ ! -d ${newDataDir} ]]; then
90+
install -d -m 0700 -o postgres -g postgres "${newDataDir}"
91+
${newPostgresql}/bin/initdb -D "${newDataDir}"
92+
${newPostgresql}/bin/pg_upgrade --old-datadir "${oldDataDir}" --new-datadir "${newDataDir}" \
93+
--old-bindir "${oldPostgresql}/bin" --new-bindir "${newPostgresql}/bin"
94+
else
95+
echo "${newDataDir} already exists"
96+
fi
97+
'';
98+
};
99+
100+
systemd.services.postgresql = {
101+
after = [ "postgresql-migrate.service" ];
102+
requires = [ "postgresql-migrate.service" ];
103+
};
104+
};
105+
};
106+
testScript =
107+
{ nodes, ... }:
108+
let
109+
pg17-configuration = "${nodes.server.system.build.toplevel}/specialisation/postgresql17";
110+
in
111+
''
112+
from pathlib import Path
113+
versions = {
114+
"15": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "15"))}],
115+
"17": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "17"))}],
116+
}
117+
extension_name = "${pname}"
118+
support_upgrade = True
119+
pg17_configuration = "${pg17-configuration}"
120+
ext_has_background_worker = ${
121+
if (installedExtension "15") ? hasBackgroundWorker then "True" else "False"
122+
}
123+
sql_test_directory = Path("${../../tests}")
124+
pg_regress_test_name = "${(installedExtension "15").pgRegressTestName or pname}"
125+
126+
${builtins.readFile ./lib.py}
127+
128+
start_all()
129+
130+
server.wait_for_unit("multi-user.target")
131+
server.wait_for_unit("postgresql.service")
132+
133+
test = PostgresExtensionTest(server, extension_name, versions, sql_test_directory, support_upgrade)
134+
135+
with subtest("Check pg_regress with postgresql 15 after extension upgrade"):
136+
test.check_pg_regress(Path("${psql_15}/lib/pgxs/src/test/regress/pg_regress"), "15", pg_regress_test_name)
137+
138+
139+
with subtest("switch to postgresql 17"):
140+
server.succeed(
141+
f"{pg17_configuration}/bin/switch-to-configuration test >&2"
142+
)
143+
144+
with subtest("Check pg_regress with postgresql 15 after extension upgrade"):
145+
test.check_pg_regress(Path("${psql_15}/lib/pgxs/src/test/regress/pg_regress"), "17", pg_regress_test_name)
146+
'';
147+
}

nix/ext/versions.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,15 @@
507507
"hash": "sha256-MC87bqgtynnDhmNZAu96jvfCpsGDCPB0g5TZfRQHd30="
508508
}
509509
},
510+
"supautils": {
511+
"3.0.1": {
512+
"postgresql": [
513+
"15",
514+
"17"
515+
],
516+
"hash": "sha256-j0iASDzmcZRLbHaS9ZNRWwzii7mcC+8wYHM0/mOLkbs="
517+
}
518+
},
510519
"timescaledb": {
511520
"2.9.1": {
512521
"postgresql": [

nix/tests/expected/supautils.out

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
select current_setting('supautils.privileged_role', false);
2+
current_setting
3+
-----------------
4+
postgres
5+
(1 row)
6+
7+
select current_setting('supautils.privileged_extensions_superuser', false);
8+
current_setting
9+
-----------------
10+
supabase_admin
11+
(1 row)
12+
13+
-- superuser can't execute COPY ... PROGRAM
14+
copy (select '') to program 'id';
15+
ERROR: COPY TO/FROM PROGRAM not allowed
16+
DETAIL: The copy to/from program utility statement is disabled
17+
\echo

nix/tests/sql/supautils.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- create a superuser role
2+
create role super2 superuser;
3+
set role super2;
4+
\echo
5+
6+
LOAD 'supautils';
7+
8+
select current_setting('supautils.privileged_role', false);
9+
10+
-- superuser can't execute COPY ... PROGRAM
11+
copy (select '') to program 'id';
12+
\echo

0 commit comments

Comments
 (0)