From e90b1eda1c43bc6114ff8f7101f9d649238de38b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= <jfroche@pyxel.be>
Date: Mon, 30 Jun 2025 22:23:12 +0200
Subject: [PATCH 1/3] feat: support multiple versions of the pg_stat_monitor
 extension

Build multiple versions of the pg_stat_monitor extension on different PostgreSQL versions.
Add test for the extensions and their upgrade on PostgreSQL 15 and 17.
---
 nix/ext/pg_stat_monitor.nix       | 111 ++++++++++++++++-----
 nix/ext/tests/pg_stat_monitor.nix | 158 ++++++++++++++++++++++++++++++
 nix/ext/versions.json             |  17 ++++
 3 files changed, 264 insertions(+), 22 deletions(-)
 create mode 100644 nix/ext/tests/pg_stat_monitor.nix

diff --git a/nix/ext/pg_stat_monitor.nix b/nix/ext/pg_stat_monitor.nix
index 742e1c0aa..2a2ce0097 100644
--- a/nix/ext/pg_stat_monitor.nix
+++ b/nix/ext/pg_stat_monitor.nix
@@ -3,36 +3,103 @@
   stdenv,
   fetchFromGitHub,
   postgresql,
+  buildEnv,
 }:
-
-stdenv.mkDerivation rec {
+let
   pname = "pg_stat_monitor";
-  version = "2.1.0";
 
-  buildInputs = [ postgresql ];
+  # Load version configuration from external file
+  allVersions = (builtins.fromJSON (builtins.readFile ./versions.json)).${pname};
 
-  src = fetchFromGitHub {
-    owner = "percona";
-    repo = pname;
-    rev = "refs/tags/${version}";
-    hash = "sha256-STJVvvrLVLe1JevNu6u6EftzAWv+X+J8lu66su7Or2s=";
-  };
+  # Filter versions compatible with current PostgreSQL version
+  supportedVersions = lib.filterAttrs (
+    _: value: builtins.elem (lib.versions.major postgresql.version) value.postgresql
+  ) allVersions;
+
+  # 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 value.revision) supportedVersions
+  );
+
+  # Build function for individual versions
+  build =
+    version: hash: revision:
+    stdenv.mkDerivation rec {
+      inherit pname version;
+
+      buildInputs = [ postgresql ];
+
+      src = fetchFromGitHub {
+        owner = "percona";
+        repo = pname;
+        rev = "refs/tags/${revision}";
+        inherit hash;
+      };
+
+      makeFlags = [ "USE_PGXS=1" ];
+
+      installPhase = ''
+        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
+        sed -e "/^default_version =/d" \
+            -e "s|^module_pathname = .*|module_pathname = '\$libdir/${pname}-${version}'|" \
+          ${pname}.control > $out/share/postgresql/extension/${pname}--${version}.control
+
+        # For the latest version, create default control file and symlink and copy SQL upgrade scripts
+        if [[ "${version}" == "${latestVersion}" ]]; then
+          {
+            echo "default_version = '${version}'"
+            cat $out/share/postgresql/extension/${pname}--${version}.control
+          } > $out/share/postgresql/extension/${pname}.control
+          ln -sfn ${pname}-${latestVersion}${postgresql.dlSuffix} $out/lib/${pname}${postgresql.dlSuffix}
+          cp *.sql $out/share/postgresql/extension
+        else
+          mv ./pg_stat_monitor--${version}.sql.in $out/share/postgresql/extension/pg_stat_monitor--${version}.sql
+        fi
+      '';
+
+      meta = with lib; {
+        description = "Query Performance Monitoring Tool for PostgreSQL";
+        homepage = "https://github.com/percona/${pname}";
+        license = licenses.postgresql;
+        broken = lib.versionOlder postgresql.version "15";
+        inherit (postgresql.meta) platforms;
+      };
+    };
+in
+buildEnv {
+  name = pname;
+  paths = packages;
 
-  makeFlags = [ "USE_PGXS=1" ];
+  pathsToLink = [
+    "/lib"
+    "/share/postgresql/extension"
+  ];
 
-  installPhase = ''
-    mkdir -p $out/{lib,share/postgresql/extension}
+  postBuild = ''
+    # Verify all expected library files are present
+    expectedFiles=${toString (numberOfVersions + 1)}
+    actualFiles=$(ls -l $out/lib/${pname}*${postgresql.dlSuffix} | wc -l)
 
-    cp *${postgresql.dlSuffix}      $out/lib
-    cp *.sql     $out/share/postgresql/extension
-    cp *.control $out/share/postgresql/extension
+    if [[ "$actualFiles" != "$expectedFiles" ]]; then
+      echo "Error: Expected $expectedFiles library files, found $actualFiles"
+      echo "Files found:"
+      ls -la $out/lib/*${postgresql.dlSuffix} || true
+      exit 1
+    fi
   '';
 
-  meta = with lib; {
-    description = "Query Performance Monitoring Tool for PostgreSQL";
-    homepage = "https://github.com/percona/${pname}";
-    platforms = postgresql.meta.platforms;
-    license = licenses.postgresql;
-    broken = lib.versionOlder postgresql.version "15";
+  passthru = {
+    inherit versions numberOfVersions;
+    pname = "${pname}-all";
+    version =
+      "multi-" + lib.concatStringsSep "-" (map (v: lib.replaceStrings [ "." ] [ "-" ] v) versions);
   };
 }
diff --git a/nix/ext/tests/pg_stat_monitor.nix b/nix/ext/tests/pg_stat_monitor.nix
new file mode 100644
index 000000000..d1aad45ce
--- /dev/null
+++ b/nix/ext/tests/pg_stat_monitor.nix
@@ -0,0 +1,158 @@
+{ self, pkgs }:
+let
+  pname = "pg_stat_monitor";
+  inherit (pkgs) lib;
+  installedExtension =
+    postgresMajorVersion: self.packages.${pkgs.system}."psql_${postgresMajorVersion}/exts/${pname}-all";
+  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;
+          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 = pname;
+  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;
+      };
+
+      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
+    ''
+      versions = {
+        "15": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "15"))}],
+        "17": [${lib.concatStringsSep ", " (map (s: ''"${s}"'') (versions "17"))}],
+      }
+
+      def run_sql(query):
+        return server.succeed(f"""sudo -u postgres psql -t -A -F\",\" -c \"{query}\" """).strip()
+
+      def check_upgrade_path(pg_version):
+        with subtest("Check ${pname} upgrade path"):
+          firstVersion = versions[pg_version][0]
+          server.succeed("sudo -u postgres psql -c 'DROP EXTENSION IF EXISTS ${pname};'")
+          run_sql(f"""CREATE EXTENSION ${pname} WITH VERSION '{firstVersion}' CASCADE;""")
+          installed_version = run_sql(r"""SELECT extversion FROM pg_extension WHERE extname = '${pname}';""")
+          assert installed_version == firstVersion, f"Expected ${pname} version {firstVersion}, but found {installed_version}"
+          for version in versions[pg_version][1:]:
+            run_sql(f"""ALTER EXTENSION ${pname} UPDATE TO '{version}';""")
+            installed_version = run_sql(r"""SELECT extversion FROM pg_extension WHERE extname = '${pname}';""")
+            assert installed_version == version, f"Expected ${pname} version {version}, but found {installed_version}"
+
+      start_all()
+
+      server.wait_for_unit("multi-user.target")
+      server.wait_for_unit("postgresql.service")
+
+      check_upgrade_path("15")
+
+      with subtest("Check ${pname} latest extension version"):
+        server.succeed("sudo -u postgres psql -c 'DROP EXTENSION ${pname};'")
+        server.succeed("sudo -u postgres psql -c 'CREATE EXTENSION ${pname} CASCADE;'")
+        installed_extensions=run_sql(r"""SELECT extname, extversion FROM pg_extension;""")
+        latestVersion = versions["15"][-1]
+        assert f"${pname},{latestVersion}" in installed_extensions
+
+      with subtest("switch to postgresql 17"):
+        server.succeed(
+          "${pg17-configuration}/bin/switch-to-configuration test >&2"
+        )
+
+      with subtest("Check ${pname} latest extension version after upgrade"):
+        installed_extensions=run_sql(r"""SELECT extname, extversion FROM pg_extension;""")
+        latestVersion = versions["17"][-1]
+        assert f"${pname},{latestVersion}" in installed_extensions
+
+      check_upgrade_path("17")
+    '';
+}
diff --git a/nix/ext/versions.json b/nix/ext/versions.json
index c77b23be6..19f366604 100644
--- a/nix/ext/versions.json
+++ b/nix/ext/versions.json
@@ -20,5 +20,22 @@
       ],
       "hash": "sha256-G0eQk2bY5CNPMeokN/nb05g03CuiplRf902YXFVQFbs="
     }
+  },
+  "pg_stat_monitor": {
+    "1.0": {
+      "postgresql": [
+        "15"
+      ],
+      "revision": "1.0.1",
+      "hash": "sha256-sQEpIknAFOmvNTX2G23X4BvMdy3Ms7sXx7hLZt8jyUk="
+    },
+    "2.1": {
+      "postgresql": [
+        "15",
+        "17"
+      ],
+      "revision": "2.1.0",
+      "hash": "sha256-STJVvvrLVLe1JevNu6u6EftzAWv+X+J8lu66su7Or2s="
+    }
   }
 }

From c72301eeb10017bf8b16a1f9a1fadc77920cd762 Mon Sep 17 00:00:00 2001
From: Sam Rose <samuel@supabase.io>
Date: Thu, 7 Aug 2025 12:21:12 -0400
Subject: [PATCH 2/3] feat: rebase on develop and suffix for testing

---
 ansible/vars.yml                  | 6 +++---
 nix/ext/tests/pg_stat_monitor.nix | 1 -
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/ansible/vars.yml b/ansible/vars.yml
index 949a76cab..96e795bbc 100644
--- a/ansible/vars.yml
+++ b/ansible/vars.yml
@@ -9,9 +9,9 @@ postgres_major:
 
 # Full version strings for each major version
 postgres_release:
-  postgresorioledb-17: "17.5.1.014-orioledb"
-  postgres17: "17.4.1.071"
-  postgres15: "15.8.1.128"
+  postgresorioledb-17: "17.5.1.014-orioledb-test-1"
+  postgres17: "17.4.1.071-test-1"
+  postgres15: "15.8.1.128-test-1"
 
 # Non Postgres Extensions
 pgbouncer_release: "1.19.0"
diff --git a/nix/ext/tests/pg_stat_monitor.nix b/nix/ext/tests/pg_stat_monitor.nix
index d1aad45ce..d1b2303b9 100644
--- a/nix/ext/tests/pg_stat_monitor.nix
+++ b/nix/ext/tests/pg_stat_monitor.nix
@@ -101,7 +101,6 @@ self.inputs.nixpkgs.lib.nixos.runTest {
           requires = [ "postgresql-migrate.service" ];
         };
       };
-
     };
   testScript =
     { nodes, ... }:

From 58fa4045319773b26349202294ca8c8bf40c83f5 Mon Sep 17 00:00:00 2001
From: Sam Rose <samuel@supabase.io>
Date: Thu, 7 Aug 2025 13:52:18 -0400
Subject: [PATCH 3/3] chore: rename to avoid conflict

---
 ansible/vars.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/ansible/vars.yml b/ansible/vars.yml
index 96e795bbc..8abaed4ec 100644
--- a/ansible/vars.yml
+++ b/ansible/vars.yml
@@ -9,9 +9,9 @@ postgres_major:
 
 # Full version strings for each major version
 postgres_release:
-  postgresorioledb-17: "17.5.1.014-orioledb-test-1"
-  postgres17: "17.4.1.071-test-1"
-  postgres15: "15.8.1.128-test-1"
+  postgresorioledb-17: "17.5.1.014-orioledb-pgsm-1"
+  postgres17: "17.4.1.071-pgsm-1"
+  postgres15: "15.8.1.128-pgsm-1"
 
 # Non Postgres Extensions
 pgbouncer_release: "1.19.0"
