From 7e1d57ef6faa5bf1084f5912f948b2dacf3f0a9a Mon Sep 17 00:00:00 2001 From: fwazb Date: Thu, 23 Jul 2026 23:16:36 -0700 Subject: [PATCH] Drop staging function before create instead of replace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloud SQL's postgres (cloudsqlsuperuser) cannot CREATE OR REPLACE a function owned by another role. Drop both overloads first so a plain CREATE works regardless of prior ownership state. Also removes the dynamic ACL normalization block — a freshly created function has no stale ACLs to scan and revoke. --- ops/gcp_leoclean_runtime_role.sql | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/ops/gcp_leoclean_runtime_role.sql b/ops/gcp_leoclean_runtime_role.sql index a383dc3..3755124 100644 --- a/ops/gcp_leoclean_runtime_role.sql +++ b/ops/gcp_leoclean_runtime_role.sql @@ -729,11 +729,12 @@ grant insert ( payload ) on kb_stage.kb_proposals to leoclean_kb_stage_owner; --- Remove the prior caller-supplied-identity overload before installing the --- session-bound form. A runtime caller can only stage as canonical agent Leo. +-- Drop both overloads so CREATE (not REPLACE) works regardless of prior +-- ownership. Cloud SQL's postgres cannot replace a function owned by another role. drop function if exists kb_stage.stage_leoclean_proposal(text, text, text, text, text, jsonb); +drop function if exists kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb); -create or replace function kb_stage.stage_leoclean_proposal( +create function kb_stage.stage_leoclean_proposal( p_proposal_type text, p_channel text, p_source_ref text, @@ -799,31 +800,9 @@ begin end $function$; --- Normalize explicit ACL entries left by CREATE OR REPLACE or an earlier grant. --- A newly-created function starts with owner EXECUTE, while CREATE OR REPLACE --- preserves an existing ACL on reruns. Revoke every scoped/default entry here --- while postgres still owns the function, so Cloud SQL's cloudsqlsuperuser --- privilege is sufficient for the revoke. -revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) - from public, leoclean_kb_runtime, leoclean_kb_stage_owner; -select format( - 'revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) from %I', - grantee.rolname - ) - from pg_catalog.pg_proc function_row - cross join lateral pg_catalog.aclexplode( - coalesce( - function_row.proacl, - pg_catalog.acldefault('f', function_row.proowner) - ) - ) as acl - join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee - where function_row.oid = 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure -\gexec - +-- Fresh function has no stale ACLs. Grant runtime, transfer ownership, done. grant execute on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) to leoclean_kb_runtime; - alter function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) owner to leoclean_kb_stage_owner; revoke create on schema kb_stage from leoclean_kb_stage_owner;