Skip to content
Open
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
101 changes: 81 additions & 20 deletions nix/ext/supautils.nix
Original file line number Diff line number Diff line change
@@ -1,35 +1,96 @@
{
pkgs,
lib,
stdenv,
fetchFromGitHub,
postgresql,
}:

stdenv.mkDerivation rec {
let
pname = "supautils";
name = pname;
version = "3.0.1";
# Load version configuration from external file
allVersions = (builtins.fromJSON (builtins.readFile ./versions.json)).${pname};

buildInputs = [ postgresql ];
# Filter versions compatible with current PostgreSQL version
supportedVersions = lib.filterAttrs (
_: value: builtins.elem (lib.versions.major postgresql.version) value.postgresql
) allVersions;

src = fetchFromGitHub {
owner = "supabase";
repo = pname;
rev = "refs/tags/v${version}";
hash = "sha256-j0iASDzmcZRLbHaS9ZNRWwzii7mcC+8wYHM0/mOLkbs=";
};
# Derived version information
versions = lib.naturalSort (lib.attrNames supportedVersions);
latestVersion = lib.last versions;
numberOfVersions = builtins.length versions;
packages = builtins.attrValues (
lib.mapAttrs (name: value: build name value.hash) supportedVersions
);

# Build function for individual versions
build =
version: hash:
stdenv.mkDerivation rec {
inherit pname version;

buildInputs = [ postgresql ];

src = fetchFromGitHub {
owner = "supabase";
repo = pname;
rev = "refs/tags/v${version}";
inherit hash;
};

installPhase = ''
mkdir -p $out/lib
installPhase = ''
runHook preInstall

mkdir -p $out/{lib,share/postgresql/extension}

# Install shared library with version suffix
mv ${pname}${postgresql.dlSuffix} $out/lib/${pname}-${version}${postgresql.dlSuffix}

# Create version-specific control file
cat <<EOF > $out/share/postgresql/extension/${pname}--${version}.control
module_pathname = '$libdir/supautils'
relocatable = false
EOF

runHook postInstall
Comment on lines +41 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, explore the file structure to understand the context
find . -type f -name "*.nix" | head -20

Repository: supabase/postgres

Length of output: 563


🏁 Script executed:

# Read the full supautils.nix file to understand the build context
wc -l nix/ext/supautils.nix

Repository: supabase/postgres

Length of output: 85


🏁 Script executed:

# Check if there are SQL files in the repository
find . -type f -name "*.sql" | grep -i supabase

Repository: supabase/postgres

Length of output: 701


🏁 Script executed:

# Search for upstream source definition in supautils.nix
cat nix/ext/supautils.nix

Repository: supabase/postgres

Length of output: 2861


🏁 Script executed:

# Search for any references to supautils SQL or control files in the codebase
rg -i "supautils\.(control|sql)" --type nix

Repository: supabase/postgres

Length of output: 43


🏁 Script executed:

# Check if there's any documentation or references about supautils extension structure
rg -i "supautils" --type md | head -20

Repository: supabase/postgres

Length of output: 1212


🌐 Web query:

supautils PostgreSQL extension GitHub supabase repository structure SQL files

💡 Result:

supabase/supautils repo — summary (where the PostgreSQL extension SQL lives)

  • Repo root: contains README.md, Makefile, META.json, LICENSE and top-level dirs .github/, nix/, src/, test/. [1]
  • PostgreSQL extension source (C/SQL and extension metadata) is in the src/ directory — that is where the extension build installs the SQL control/script files used by CREATE EXTENSION. [1]
  • Docs / overview (features, install, config keys) are in README.md on the repo front page. [1]

Source:
[1] supabase/supautils (GitHub repository) — repository file listing and README.


🏁 Script executed:

# Check versions.json to understand what versions are being built
cat nix/ext/versions.json | rg -A 5 "supautils"

Repository: supabase/postgres

Length of output: 150


🏁 Script executed:

# Look at how other extensions handle SQL files and control files
cat nix/ext/pg_net.nix | head -80

Repository: supabase/postgres

Length of output: 2320


🏁 Script executed:

# Check if there are any SQL files mentioned in the extension builds
rg "\.sql" nix/ext/*.nix | head -20

Repository: supabase/postgres

Length of output: 1963


🏁 Script executed:

# Let me check the documentation about supautils updates to understand the structure
cat nix/docs/update-extension.md

Repository: supabase/postgres

Length of output: 7041


🏁 Script executed:

# Look at what pg_graphql does since it's also from supabase
head -100 nix/ext/pg_graphql/default.nix | tail -60

Repository: supabase/postgres

Length of output: 2423


🏁 Script executed:

# Check if the postBuild in supautils actually handles the versioning correctly
# by looking at the full flow - does it properly create default_version?
sed -n '64,88p' nix/ext/supautils.nix

Repository: supabase/postgres

Length of output: 742


🌐 Web query:

site:github.com supabase/supautils repository structure src control.in

💡 Result:

The supabase/supautils repository has a top-level src/ directory, but there is no file named control.in in that repository tree (no matching file found). [1][2]

Sources:

  • supabase/supautils repository (shows src/ at top level). [1]
  • supabase/supautils — src/ tree view (no control.in file present). [2]

🏁 Script executed:

# Check if there are any comments in the supautils.nix that explain why SQL files aren't copied
grep -B5 -A5 "control file" nix/ext/supautils.nix

Repository: supabase/postgres

Length of output: 985


🏁 Script executed:

# Look at how pg_graphql (also a Supabase pgrx extension) handles control files for reference
grep -A10 "postInstall" nix/ext/pg_graphql/default.nix | head -30

Repository: supabase/postgres

Length of output: 593


🏁 Script executed:

# Search for any SQL-related patterns in nix files that might indicate what extensions typically need
rg "sql.*\.sql" nix/ext/ --type nix | grep -i "supautils|pg_net|vault" | head -20

Repository: supabase/postgres

Length of output: 43


🌐 Web query:

supautils PostgreSQL extension Rust C source code SQL migration files

💡 Result:

You can find the supautils source and SQL files on the project’s GitHub repo:

Additional package/extension metadata pages:

  • Trunk package info for supautils. [2]
  • Pigsty package metadata (versions for multiple PG releases). [3]

If you want I can list or show specific files (e.g., src/*.c, extension control/SQL files) from the repo — tell me which files or paths you want.


🏁 Script executed:

# Check if there's a Makefile or build-related file that shows what supautils builds
# Since we can see it's fetched from GitHub, the typical structure would have src files
# Let's check if there's any documentation about what supautils provides
grep -r "supautils" nix/docs/ -A 3 -B 3

Repository: supabase/postgres

Length of output: 2153


Install upstream SQL migration files during installPhase.

The current installPhase creates a minimal control file but does not copy SQL migration scripts (e.g., supautils--*.sql files) from the upstream source. Unlike the default_version (which is correctly added in postBuild), missing SQL files will cause CREATE EXTENSION and upgrade operations to fail. Other extensions in this repo (pg_net, vault, pg_graphql, etc.) consistently copy these files. If the upstream supautils repository ships migration scripts in its src/ directory, they must be installed alongside the compiled library.

🤖 Prompt for AI Agents
In `@nix/ext/supautils.nix` around lines 41 - 55, The installPhase currently
writes the control file but fails to install upstream SQL migration scripts, so
add steps in installPhase to copy the extension SQL files (e.g.,
supautils--*.sql and any supautils--*.sql.gz) from the build/source directory
(likely src/) into $out/share/postgresql/extension/ alongside the control file;
ensure files include the versioned filenames that match ${pname}--${version}.sql
and any default_version.sql if present, and keep the existing mv of the shared
library and the control file creation intact (refer to installPhase, ${pname},
${version}, and the $out/share/postgresql/extension destination).

'';

meta = with lib; {
description = "PostgreSQL extension for enhanced security";
homepage = "https://github.com/supabase/${pname}";
maintainers = with maintainers; [ steve-chavez ];
inherit (postgresql.meta) platforms;
license = licenses.postgresql;
};
};
in
pkgs.buildEnv {
name = pname;
paths = packages;
pathsToLink = [
"/lib"
"/share/postgresql/extension"
];
postBuild = ''
# Create symlinks to latest version for library and control file
ln -sfn ${pname}-${latestVersion}${postgresql.dlSuffix} $out/lib/${pname}${postgresql.dlSuffix}

install -D *${postgresql.dlSuffix} -t $out/lib
# Create default control file pointing to latest
{
echo "default_version = '${latestVersion}'"
cat $out/share/postgresql/extension/${pname}--${latestVersion}.control
} > $out/share/postgresql/extension/${pname}.control
'';

meta = with lib; {
description = "PostgreSQL extension for enhanced security";
homepage = "https://github.com/supabase/${pname}";
maintainers = with maintainers; [ steve-chavez ];
platforms = postgresql.meta.platforms;
license = licenses.postgresql;
passthru = {
inherit versions numberOfVersions;
pname = "${pname}-all";
defaultSettings = {
session_preload_libraries = "supautils";
"supautils.disable_program" = "true";
"supautils.privileged_role" = "privileged_role";
};
version =
"multi-" + lib.concatStringsSep "-" (map (v: lib.replaceStrings [ "." ] [ "-" ] v) versions);
};
}
145 changes: 145 additions & 0 deletions nix/ext/tests/supautils.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{ self, pkgs }:
let
pname = "supautils";
inherit (pkgs) lib;
installedExtension =
postgresMajorVersion:
self.legacyPackages.${pkgs.system}."psql_${postgresMajorVersion}".exts."${pname}";
versions = postgresqlMajorVersion: (installedExtension postgresqlMajorVersion).versions;
postgresqlWithExtension =
postgresql:
let
majorVersion = lib.versions.major postgresql.version;
pkg = pkgs.buildEnv {
name = "postgresql-${majorVersion}-${pname}";
paths = [
postgresql
postgresql.lib
(installedExtension majorVersion)
];
passthru = {
inherit (postgresql) version psqlSchema;
installedExtensions = [ (installedExtension majorVersion) ];
lib = pkg;
withPackages = _: pkg;
withJIT = pkg;
withoutJIT = pkg;
};
nativeBuildInputs = [ pkgs.makeWrapper ];
pathsToLink = [
"/"
"/bin"
"/lib"
];
postBuild = ''
wrapProgram $out/bin/postgres --set NIX_PGLIBDIR $out/lib
wrapProgram $out/bin/pg_ctl --set NIX_PGLIBDIR $out/lib
wrapProgram $out/bin/pg_upgrade --set NIX_PGLIBDIR $out/lib
'';
};
in
pkg;
psql_15 = postgresqlWithExtension self.packages.${pkgs.system}.postgresql_15;
psql_17 = postgresqlWithExtension self.packages.${pkgs.system}.postgresql_17;
in
self.inputs.nixpkgs.lib.nixos.runTest {
name = pname;
hostPkgs = pkgs;
nodes.server =
{ config, ... }:
{
services.postgresql = {
enable = true;
package = (postgresqlWithExtension psql_15);
settings = {
shared_preload_libraries = "supautils";
"supautils.privileged_extensions" =
"address_standardizer, address_standardizer_data_us, autoinc, bloom, btree_gin, btree_gist, citext, cube, dblink, dict_int, dict_xsyn, earthdistance, fuzzystrmatch, hstore, http, hypopg, index_advisor, insert_username, intarray, isn, ltree, moddatetime, orioledb, pg_buffercache, pg_cron, pg_graphql, pg_hashids, pg_jsonschema, pg_net, pg_prewarm, pg_repack, pg_stat_monitor, pg_stat_statements, pg_tle, pg_trgm, pg_walinspect, pgaudit, pgcrypto, pgjwt, pgroonga, pgroonga_database, pgrouting, pgrowlocks, pgsodium, pgstattuple, pgtap, plcoffee, pljava, plls, plpgsql_check, plv8, postgis, postgis_raster, postgis_sfcgal, postgis_tiger_geocoder, postgis_topology, postgres_fdw, refint, rum, seg, sslinfo, supabase_vault, supautils, tablefunc, tcn, timescaledb, tsm_system_rows, tsm_system_time, unaccent, uuid-ossp, vector, wrappers";
};
};

specialisation.postgresql17.configuration = {
services.postgresql = {
package = lib.mkForce psql_17;
settings = {
"supautils.privileged_extensions" =
lib.mkForce "address_standardizer, address_standardizer_data_us, autoinc, bloom, btree_gin, btree_gist, citext, cube, dblink, dict_int, dict_xsyn, earthdistance, fuzzystrmatch, hstore, http, hypopg, index_advisor, insert_username, intarray, isn, ltree, moddatetime, orioledb, pg_buffercache, pg_cron, pg_graphql, pg_hashids, pg_jsonschema, pg_net, pg_prewarm, pg_repack, pg_stat_monitor, pg_stat_statements, pg_tle, pg_trgm, pg_walinspect, pgaudit, pgcrypto, pgjwt, pgroonga, pgroonga_database, pgrouting, pgrowlocks, pgsodium, pgstattuple, pgtap, plcoffee, pljava, plls, plpgsql_check, postgis, postgis_raster, postgis_sfcgal, postgis_tiger_geocoder, postgis_topology, postgres_fdw, refint, rum, seg, sslinfo, supabase_vault, supautils, tablefunc, tcn, tsm_system_rows, tsm_system_time, unaccent, uuid-ossp, vector, wrappers";
};
};

systemd.services.postgresql-migrate = {
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "postgres";
Group = "postgres";
StateDirectory = "postgresql";
WorkingDirectory = "${builtins.dirOf config.services.postgresql.dataDir}";
};
script =
let
oldPostgresql = psql_15;
newPostgresql = psql_17;
oldDataDir = "${builtins.dirOf config.services.postgresql.dataDir}/${oldPostgresql.psqlSchema}";
newDataDir = "${builtins.dirOf config.services.postgresql.dataDir}/${newPostgresql.psqlSchema}";
in
''
if [[ ! -d ${newDataDir} ]]; then
install -d -m 0700 -o postgres -g postgres "${newDataDir}"
${newPostgresql}/bin/initdb -D "${newDataDir}"
${newPostgresql}/bin/pg_upgrade --old-datadir "${oldDataDir}" --new-datadir "${newDataDir}" \
--old-bindir "${oldPostgresql}/bin" --new-bindir "${newPostgresql}/bin" \
--old-options='-c shared_preload_libraries=supautils' --new-options='-c shared_preload_libraries=supautils'
else
echo "${newDataDir} already exists"
fi
'';
};

systemd.services.postgresql = {
after = [ "postgresql-migrate.service" ];
requires = [ "postgresql-migrate.service" ];
};
};
};
testScript =
{ nodes, ... }:
let
pg17-configuration = "${nodes.server.system.build.toplevel}/specialisation/postgresql17";
in
''
from pathlib import Path
versions = {
"15": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "15"))}],
"17": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "17"))}],
}
extension_name = "${pname}"
support_upgrade = False
pg17_configuration = "${pg17-configuration}"
ext_has_background_worker = ${
if (installedExtension "15") ? hasBackgroundWorker then "True" else "False"
}
sql_test_directory = Path("${../../tests}")
pg_regress_test_name = "${(installedExtension "15").pgRegressTestName or pname}"

${builtins.readFile ./lib.py}

start_all()

server.wait_for_unit("multi-user.target")
server.wait_for_unit("postgresql.service")

test = PostgresExtensionTest(server, extension_name, versions, sql_test_directory, support_upgrade)

with subtest("Check pg_regress with postgresql 15 after extension upgrade"):
test.check_pg_regress(Path("${psql_15}/lib/pgxs/src/test/regress/pg_regress"), "15", pg_regress_test_name)

with subtest("switch to postgresql 17"):
server.succeed(
f"{pg17_configuration}/bin/switch-to-configuration test >&2"
)

with subtest("Check pg_regress with postgresql 17 after extension upgrade"):
test.check_pg_regress(Path("${psql_17}/lib/pgxs/src/test/regress/pg_regress"), "17", pg_regress_test_name)
'';
}
9 changes: 9 additions & 0 deletions nix/ext/versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,15 @@
"hash": "sha256-MC87bqgtynnDhmNZAu96jvfCpsGDCPB0g5TZfRQHd30="
}
},
"supautils": {
"3.0.1": {
"postgresql": [
"15",
"17"
],
"hash": "sha256-j0iASDzmcZRLbHaS9ZNRWwzii7mcC+8wYHM0/mOLkbs="
}
},
"timescaledb": {
"2.9.1": {
"postgresql": [
Expand Down
4 changes: 4 additions & 0 deletions nix/packages/postgres.nix
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@
pgbin
(makeReceipt pgbin ourExts)
];
passthru = postgresql.passthru // {
withPackages = f: postgresql.withPackages f;
installedExtensions = postgres-pkgs;
};
};

# Create an attribute set, containing all the relevant packages for a
Expand Down
2 changes: 2 additions & 0 deletions nix/postgresql/generic.nix
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ let
withJIT = if jitSupport then this else jitToggle;
withoutJIT = if jitSupport then jitToggle else this;

installedExtensions = [ ];

dlSuffix = if olderThan "16" then ".so" else stdenv.hostPlatform.extensions.sharedLibrary;
inherit isOrioleDB;

Expand Down
6 changes: 3 additions & 3 deletions nix/tests/expected/roles.out
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ select
from pg_roles r
where rolname not in ('pg_create_subscription', 'pg_maintain', 'pg_use_reserved_connections')
order by rolname;
rolname | rolconfig
----------------------------+---------------------------------------------------------------------------------
rolname | rolconfig
----------------------------+------------------------------------------------------------------------------------------
anon | {statement_timeout=3s}
authenticated | {statement_timeout=8s}
authenticator | {session_preload_libraries=safeupdate,statement_timeout=8s,lock_timeout=8s}
authenticator | {"session_preload_libraries=supautils, safeupdate",statement_timeout=8s,lock_timeout=8s}
dashboard_user |
pg_checkpoint |
pg_database_owner |
Expand Down
3 changes: 2 additions & 1 deletion nix/tests/expected/z_15_ext_interface.out
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ order by
-----------------
pg_cron
pgjwt
supautils
tsm_system_time
wal2json
(4 rows)
(5 rows)

/*

Expand Down
39 changes: 39 additions & 0 deletions nix/tests/expected/z_15_supautils.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
begin;
load 'supautils';
-- verify that supautils configuration parameters exist
select current_setting('supautils.privileged_extensions', true) is not null as has_privileged_extensions;
has_privileged_extensions
---------------------------
t
(1 row)

select current_setting('supautils.privileged_role', true) is not null as has_privileged_role;
has_privileged_role
---------------------
t
(1 row)

-- switch to postgres role and verify access to settings
set role postgres;
select current_setting('supautils.privileged_extensions', true) as privileged_extensions;
privileged_extensions
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
address_standardizer, address_standardizer_data_us, autoinc, bloom, btree_gin, btree_gist, citext, cube, dblink, dict_int, dict_xsyn, earthdistance, fuzzystrmatch, hstore, http, hypopg, index_advisor, insert_username, intarray, isn, ltree, moddatetime, orioledb, pg_buffercache, pg_cron, pg_graphql, pg_hashids, pg_jsonschema, pg_net, pg_prewarm, pg_repack, pg_stat_monitor, pg_stat_statements, pg_tle, pg_trgm, pg_walinspect, pgaudit, pgcrypto, pgjwt, pgroonga, pgroonga_database, pgrouting, pgrowlocks, pgsodium, pgstattuple, pgtap, plcoffee, pljava, plls, plpgsql_check, plv8, postgis, postgis_raster, postgis_sfcgal, postgis_tiger_geocoder, postgis_topology, postgres_fdw, refint, rum, seg, sslinfo, supabase_vault, supautils, tablefunc, tcn, timescaledb, tsm_system_rows, tsm_system_time, unaccent, uuid-ossp, vector, wrappers
(1 row)

-- create a simple schema to verify normal operations work
create schema v;
create table v.test_table (
id serial primary key,
data text
);
insert into v.test_table (data)
values ('test1'), ('test2');
select * from v.test_table order by id;
id | data
----+-------
1 | test1
2 | test2
(2 rows)

rollback;
3 changes: 2 additions & 1 deletion nix/tests/expected/z_17_ext_interface.out
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ order by
pg_cron
pgjwt
postgis_tiger_geocoder
supautils
tsm_system_time
wal2json
(5 rows)
(6 rows)

/*

Expand Down
Loading
Loading