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
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ begin
this update is backwards compatible with version 1.4.4 but should be removed once we're on
physical backups everywhere
*/
-- Detach and delete the official function
if extversion = '1.4.4' then
alter extension pgmq drop function pgmq.drop_queue;
drop function pgmq.drop_queue;
else -- 1.5.1+
alter extension pgmq drop function pgmq.drop_queue(TEXT);
drop function pgmq.drop_queue(TEXT);
end if;
-- detach both historical drop_queue signatures, if present as extension members
if to_regprocedure('pgmq.drop_queue(text)') is not null then
alter extension pgmq drop function pgmq.drop_queue(text);
end if;
if to_regprocedure('pgmq.drop_queue(text, boolean)') is not null then
alter extension pgmq drop function pgmq.drop_queue(text, boolean);
end if;

drop function if exists pgmq.drop_queue(text);
drop function if exists pgmq.drop_queue(text, boolean);

-- Create and reattach the patched function
CREATE FUNCTION pgmq.drop_queue(queue_name TEXT)
Expand Down Expand Up @@ -73,7 +75,7 @@ BEGIN
join pg_depend d on c.oid = d.objid
join pg_extension e on d.refobjid = e.oid
where c.relname = qtable_seq and e.extname = 'pgmq'
) then
) then
EXECUTE FORMAT(
$QUERY$
ALTER EXTENSION pgmq DROP SEQUENCE pgmq.%I
Expand Down Expand Up @@ -143,11 +145,17 @@ BEGIN
END;
$func$ LANGUAGE plpgsql;

if extversion = '1.4.4' then
alter extension pgmq add function pgmq.drop_queue;
else -- 1.5.1+
alter extension pgmq add function pgmq.drop_queue(TEXT);
end if;
alter extension pgmq add function pgmq.drop_queue(text);

-- compat shim, kept callable with the historical 2-arg signature
CREATE OR REPLACE FUNCTION pgmq.drop_queue(queue_name TEXT, partitioned BOOLEAN)
RETURNS BOOLEAN AS $shim$
BEGIN
RETURN pgmq.drop_queue(queue_name);
END;
$shim$ LANGUAGE plpgsql;

alter extension pgmq add function pgmq.drop_queue(text, boolean);


update pg_extension set extowner = 'postgres'::regrole where extname = 'pgmq';
Expand Down
31 changes: 31 additions & 0 deletions nix/ext/pgmq/0002-detach-drop_queue-shim-before-drop.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Supabase <support@supabase.com>
Date: Wed, 8 Oct 2026 00:00:00 +0000
Subject: [PATCH] fix: detach drop_queue(text, boolean) before dropping it

after-create.sql now attaches drop_queue(text, boolean) to the pgmq
extension. A plain DROP FUNCTION IF EXISTS can't remove an extension
member, so ALTER EXTENSION UPDATE would abort here for anyone
upgrading from pgmq 1.4.4. Detach it first if it's currently a member.
---
pgmq-extension/sql/pgmq--1.4.5--1.5.0.sql | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/pgmq-extension/sql/pgmq--1.4.5--1.5.0.sql b/pgmq-extension/sql/pgmq--1.4.5--1.5.0.sql
--- a/pgmq-extension/sql/pgmq--1.4.5--1.5.0.sql
+++ b/pgmq-extension/sql/pgmq--1.4.5--1.5.0.sql
@@ -104,5 +104,12 @@ END;
$$ LANGUAGE plpgsql;

+DO $$
+BEGIN
+ IF to_regprocedure('pgmq.drop_queue(text, boolean)') IS NOT NULL THEN
+ ALTER EXTENSION pgmq DROP FUNCTION pgmq.drop_queue(text, boolean);
+ END IF;
+END $$;
+
DROP FUNCTION IF EXISTS pgmq.drop_queue(TEXT, BOOLEAN);

CREATE FUNCTION pgmq.drop_queue(queue_name TEXT, partitioned BOOLEAN)
--
2.51.0
1 change: 1 addition & 0 deletions nix/ext/pgmq/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ let

patches = lib.optionals (version == latestVersion) [
./0001-fix-replace-drop_queue-function-if-exists.patch
./0002-detach-drop_queue-shim-before-drop.patch
];

buildPhase = ''
Expand Down
95 changes: 95 additions & 0 deletions nix/ext/tests/pgmq-drop-queue-overload.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{ self, pkgs }:
let
pname = "pgmq";
inherit (pkgs) lib;
system = pkgs.pkgsLinux.stdenv.hostPlatform.system;
testLib = import ./lib.nix { inherit self pkgs; };

installedExtension = self.legacyPackages.${system}."psql_15".exts."${pname}";
versions = installedExtension.versions;
in
pkgs.testers.runNixOSTest {
name = "pgmq-drop-queue-overload";
nodes.server =
{ ... }:
{
imports = [
(testLib.makeSupabaseTestConfig {
majorVersion = "15";
})
];
};
testScript =
{ ... }:
let
versionList = lib.concatStringsSep ", " (map (v: ''"${v}"'') versions);
in
''
versions = [${versionList}]

def sql(query):
return server.succeed(
"psql -U supabase_admin -d postgres -t -A -F',' -c \"" + query.replace('"', '\\"') + "\""
).strip()

def drop_queue_overloads():
# owned flag first: pg_get_function_identity_arguments() can itself
# contain a comma ("queue_name text, partitioned boolean"), so put the
# single-char flag first and split on the first comma only.
out = sql(
"select (d.objid is not null), pg_get_function_identity_arguments(p.oid) "
"from pg_proc p "
"left join pg_depend d on d.objid = p.oid and d.deptype = 'e' "
" and d.refobjid = (select oid from pg_extension where extname = 'pgmq') "
"where p.pronamespace = 'pgmq'::regnamespace and p.proname = 'drop_queue' "
"order by 2;"
)
return [line.split(",", 1) for line in out.splitlines() if line]

# every calling convention drop_queue has ever supported still works
def check_callers(qname):
sql(f"select pgmq.create('{qname}_a'); select pgmq.drop_queue('{qname}_a');")
sql(f"select pgmq.create('{qname}_b'); select pgmq.drop_queue('{qname}_b', false);")
# WRONG flag on purpose (queue isn't partitioned) - must still
# succeed, safely ignored in favour of pgmq.meta
sql(f"select pgmq.create('{qname}_c'); select pgmq.drop_queue('{qname}_c', true);")
sql(
f"select pgmq.create('{qname}_d'); "
f"select pgmq.drop_queue(queue_name => '{qname}_d', partitioned => true);"
)

start_all()
server.wait_for_unit("supabase-db-init.service")

# fresh install of each pinned version: check the overload split, then the callers
for version in versions:
with subtest(f"install pgmq {version}"):
server.succeed("psql -U supabase_admin -d postgres -c 'DROP EXTENSION IF EXISTS pgmq;'")
server.succeed(
f"psql -U supabase_admin -d postgres -c \"CREATE EXTENSION pgmq WITH VERSION '{version}' CASCADE;\""
)

overloads = drop_queue_overloads()
print(f"[{version}] drop_queue overloads: {overloads}")
assert overloads == [
["t", "queue_name text"],
["t", "queue_name text, partitioned boolean"],
], (
f"[{version}] expected both drop_queue(text) and "
f"drop_queue(text, boolean) extension-owned, got: {overloads}"
)

check_callers(f"q_{version.replace('.', '_')}")

# DROP EXTENSION must remove both signatures, leaving nothing behind
# for a later CREATE EXTENSION to collide with
with subtest("DROP EXTENSION leaves no drop_queue behind"):
server.succeed("psql -U supabase_admin -d postgres -c 'DROP EXTENSION pgmq;'")
remaining = sql(
"select count(*) from pg_proc p "
"join pg_namespace n on n.oid = p.pronamespace "
"where n.nspname = 'pgmq' and p.proname = 'drop_queue';"
)
assert remaining == "0", f"expected no leftover drop_queue, got {remaining} rows"
'';
}
Loading