From fcbc87571dce1b51ab210edea76c38a4dc567490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Tue, 3 Jun 2025 17:58:15 +0200 Subject: [PATCH] feat: multiple versions for the pg_net extension Build multiple versions of the pg_net extension to on different PostgreSQL versions. Add test for the extensions and their upgrade on PostgreSQL 15 and 17. --- flake.nix | 3 +- nix/ext/pg_net.nix | 106 ++++++++++++++++++++++++------- nix/ext/versions.json | 74 ++++++++++++++++++++++ nix/tests/pg_net.nix | 143 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 304 insertions(+), 22 deletions(-) create mode 100644 nix/ext/versions.json create mode 100644 nix/tests/pg_net.nix diff --git a/flake.nix b/flake.nix index d49c9e3fa..21bfdf622 100644 --- a/flake.nix +++ b/flake.nix @@ -221,7 +221,7 @@ makeOurPostgresPkgsSet = version: (builtins.listToAttrs (map (drv: - { name = drv.pname; value = drv; } + { name = drv.pname or drv.name; value = drv; } ) (makeOurPostgresPkgs version))) // { recurseForDerivations = true; }; @@ -1376,6 +1376,7 @@ psql_15 = makeCheckHarness basePackages.psql_15.bin; psql_17 = makeCheckHarness basePackages.psql_17.bin; psql_orioledb-17 = makeCheckHarness basePackages.psql_orioledb-17.bin; + pg_net = import ./nix/tests/pg_net.nix { inherit self; inherit pkgs; }; }; # Apps is a list of names of things that can be executed with 'nix run'; diff --git a/nix/ext/pg_net.nix b/nix/ext/pg_net.nix index 0f20163e5..83ff29d44 100644 --- a/nix/ext/pg_net.nix +++ b/nix/ext/pg_net.nix @@ -1,32 +1,96 @@ -{ lib, stdenv, fetchFromGitHub, curl, postgresql }: +{ pkgs, lib, stdenv, fetchFromGitHub, curl, postgresql, libuv }: -stdenv.mkDerivation rec { +let pname = "pg_net"; - version = "0.14.0"; + build = version: hash: + stdenv.mkDerivation rec { + inherit pname version; - buildInputs = [ curl postgresql ]; + buildInputs = [ curl postgresql ] + ++ lib.optional (version == "0.6") libuv; - src = fetchFromGitHub { - owner = "supabase"; - repo = pname; - rev = "refs/tags/v${version}"; - hash = "sha256-c1pxhTyrE5j6dY+M5eKAboQNofIORS+Dccz+7HKEKQI="; - }; + src = fetchFromGitHub { + owner = "supabase"; + repo = pname; + rev = "refs/tags/v${version}"; + inherit hash; + }; + + buildPhase = '' + make PG_CONFIG=${postgresql}/bin/pg_config + ''; + + postPatch = lib.optionalString (version == "0.6") '' + # handle collision with pg_net 0.10.0 + rm sql/pg_net--0.2--0.3.sql + rm sql/pg_net--0.4--0.5.sql + rm sql/pg_net--0.5.1--0.6.sql + '' + lib.optionalString (version == "0.7.1") '' + # handle collision with pg_net 0.10.0 + rm sql/pg_net--0.5.1--0.6.sql + ''; + + env.NIX_CFLAGS_COMPILE = "-Wno-error"; + + installPhase = '' + mkdir -p $out/{lib,share/postgresql/extension} + + # Install versioned library + install -Dm755 ${pname}${postgresql.dlSuffix} $out/lib/${pname}-${version}${postgresql.dlSuffix} + + if [ -f sql/${pname}.sql ]; then + cp sql/${pname}.sql $out/share/postgresql/extension/${pname}--${version}.sql + else + cp sql/${pname}--${version}.sql $out/share/postgresql/extension/${pname}--${version}.sql + fi + + # Install upgrade scripts + find . -name '${pname}--*--*.sql' -exec install -Dm644 {} $out/share/postgresql/extension/ \; - env.NIX_CFLAGS_COMPILE = "-Wno-error"; + # Create versioned control file with modified module path + sed -e "/^default_version =/d" \ + -e "s|^module_pathname = .*|module_pathname = '\$libdir/${pname}'|" \ + ${pname}.control > $out/share/postgresql/extension/${pname}--${version}.control + ''; - installPhase = '' - mkdir -p $out/{lib,share/postgresql/extension} + meta = with lib; { + description = "Async networking for Postgres"; + homepage = "https://github.com/supabase/pg_net"; + platforms = postgresql.meta.platforms; + license = licenses.postgresql; + }; + }; + allVersions = (builtins.fromJSON (builtins.readFile ./versions.json)).pg_net; + supportedVersions = lib.filterAttrs (_: value: + builtins.elem (lib.versions.major postgresql.version) value.postgresql) + allVersions; + 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); +in pkgs.buildEnv { + name = pname; + paths = packages; + postBuild = '' + { + echo "default_version = '${latestVersion}'" + cat $out/share/postgresql/extension/${pname}--${latestVersion}.control + } > $out/share/postgresql/extension/${pname}.control + ln -sfn ${pname}-${latestVersion}${postgresql.dlSuffix} $out/lib/${pname}${postgresql.dlSuffix} - cp *${postgresql.dlSuffix} $out/lib - cp sql/*.sql $out/share/postgresql/extension - cp *.control $out/share/postgresql/extension + # checks + (set -x + test "$(ls -A $out/lib/${pname}*${postgresql.dlSuffix} | wc -l)" = "${ + toString (numberOfVersions + 1) + }" + ) ''; - meta = with lib; { - description = "Async networking for Postgres"; - homepage = "https://github.com/supabase/pg_net"; - platforms = postgresql.meta.platforms; - license = licenses.postgresql; + passthru = { + inherit versions numberOfVersions; + pname = "${pname}-all"; + version = "multi-" + lib.concatStringsSep "-" + (map (v: lib.replaceStrings [ "." ] [ "-" ] v) versions); }; } diff --git a/nix/ext/versions.json b/nix/ext/versions.json new file mode 100644 index 000000000..e16475a3a --- /dev/null +++ b/nix/ext/versions.json @@ -0,0 +1,74 @@ +{ + "pg_net": { + "0.1": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-geXGMb9MxU2vVB3ZBkGEwNqHixPbqjywyUumk7kbdbQ=" + }, + "0.2": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-ArJmZTh7rc6OgvA6RIndMqcDRJl91QPt6pgEeCuHA6M=" + }, + "0.6": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-SpQbF/ZeAVa8zf0+N6uluHrjpmGy0NLd2/hvyiOyNsY=" + }, + "0.7": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-FRaTZPCJQPYAFmsJg22hYJJ0+gH1tMdDQoCQgiqEnaA=" + }, + "0.7.1": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-VScRKzY/skQu9SWGx9iZvifH7pv7SRXcvLfybB+XX4Q=" + }, + "0.8.0": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-ZPsRPWV1G3lMM2mT+H139Wvgoy8QnmeUbzEnGeDJmZA=" + }, + "0.10.0": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-R9Mzw5gvV7b2R59LTOzuOc0AI99+3ncFNzijI4mySUg=" + }, + "0.11.0": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-XN441jXK1q+I/LZRNwvzbSsebXHgZ8iYsslZvcPFlAs=" + }, + "0.13.0": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-FRaTZPCJQPYAFmsJg22hYJJ0+gH1tMdDQoCQgiqEnaA=" + }, + "0.14.0": { + "postgresql": [ + "15", + "17" + ], + "hash": "sha256-c1pxhTyrE5j6dY+M5eKAboQNofIORS+Dccz+7HKEKQI=" + } + } +} diff --git a/nix/tests/pg_net.nix b/nix/tests/pg_net.nix new file mode 100644 index 000000000..8ea060f32 --- /dev/null +++ b/nix/tests/pg_net.nix @@ -0,0 +1,143 @@ +{ self, pkgs }: +let + inherit (pkgs) lib; + installedExtension = postgresMajorVersion: + self.packages.${pkgs.system}."psql_${postgresMajorVersion}/exts/pg_net-all"; + versions = (installedExtension "17").versions; + firstVersion = lib.head versions; + latestVersion = lib.last versions; + postgresqlWithExtension = postgresql: + let + majorVersion = lib.versions.major postgresql.version; + pkg = pkgs.buildEnv { + name = "postgresql-${majorVersion}-pg_net"; + paths = [ postgresql postgresql.lib (installedExtension majorVersion) ]; + passthru = { + inherit (postgresql) version psqlSchema; + lib = pkg; + withPackages = _: 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; +in self.inputs.nixpkgs.lib.nixos.runTest { + name = "pg_net"; + hostPkgs = pkgs; + nodes.server = { config, ... }: { + virtualisation = { + forwardPorts = [{ + from = "host"; + host.port = 13022; + guest.port = 22; + }]; + }; + services.openssh = { enable = true; }; + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIo+ulCUfJjnCVgfM4946Ih5Nm8DeZZiayYeABHGPEl7 jfroche" + ]; + + services.postgresql = { + enable = true; + package = + postgresqlWithExtension self.packages.${pkgs.system}.postgresql_15; + settings = { shared_preload_libraries = "pg_net"; }; + }; + + specialisation.postgresql17.configuration = { + services.postgresql = { + package = lib.mkForce + (postgresqlWithExtension self.packages.${pkgs.system}.postgresql_17); + }; + + 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 = + postgresqlWithExtension self.packages.${pkgs.system}.postgresql_15; + newPostgresql = + postgresqlWithExtension self.packages.${pkgs.system}.postgresql_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" + 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 '' + def run_sql(query): + return server.succeed(f"""sudo -u postgres psql -t -A -F\",\" -c \"{query}\" """).strip() + + def check_upgrade_path(): + with subtest("Check pg_net upgrade path"): + server.succeed("sudo -u postgres psql -c 'DROP EXTENSION IF EXISTS pg_net;'") + run_sql(r"""CREATE EXTENSION pg_net WITH VERSION \"${firstVersion}\";""") + installed_version = run_sql(r"""SELECT extversion FROM pg_extension WHERE extname = 'pg_net';""") + assert installed_version == "${firstVersion}", f"Expected pg_net version ${firstVersion}, but found {installed_version}" + for version in [${ + lib.concatStringsSep ", " (map (s: ''"${s}"'') versions) + }][1:]: + run_sql(f"""ALTER EXTENSION pg_net UPDATE TO '{version}';""") + installed_version = run_sql(r"""SELECT extversion FROM pg_extension WHERE extname = 'pg_net';""") + assert installed_version == version, f"Expected pg_net version {version}, but found {installed_version}" + + start_all() + + server.wait_for_unit("multi-user.target") + server.wait_for_unit("postgresql.service") + + check_upgrade_path() + + with subtest("Check pg_net latest extension version"): + server.succeed("sudo -u postgres psql -c 'DROP EXTENSION pg_net;'") + server.succeed("sudo -u postgres psql -c 'CREATE EXTENSION pg_net;'") + installed_extensions=run_sql(r"""SELECT extname, extversion FROM pg_extension;""") + assert "pg_net,${latestVersion}" in installed_extensions + + with subtest("switch to multiple node configuration"): + server.succeed( + "${pg17-configuration}/bin/switch-to-configuration test >&2" + ) + + with subtest("Check pg_net latest extension version"): + installed_extensions=run_sql(r"""SELECT extname, extversion FROM pg_extension;""") + assert "pg_net,${latestVersion}" in installed_extensions + + check_upgrade_path() + ''; +}