Handle `ALTER FUNCTION OWNER TO `
(
state: &SharedState,
identity: &AuthenticatedIdentity,
parts: &[&str],
)
| 12 | |
| 13 | /// Handle `ALTER FUNCTION <name> OWNER TO <new_owner>` |
| 14 | pub fn alter_function( |
| 15 | state: &SharedState, |
| 16 | identity: &AuthenticatedIdentity, |
| 17 | parts: &[&str], |
| 18 | ) -> PgWireResult<Vec<Response>> { |
| 19 | require_tenant_admin(identity, "alter functions")?; |
| 20 | |
| 21 | if parts.len() < 4 { |
| 22 | return Err(sqlstate_error( |
| 23 | "42601", |
| 24 | "syntax: ALTER FUNCTION <name> OWNER TO <user> | SET (FUEL=N, MEMORY=N)", |
| 25 | )); |
| 26 | } |
| 27 | |
| 28 | let name = parts[2].to_lowercase(); |
| 29 | let action = parts[3].to_uppercase(); |
| 30 | |
| 31 | // ALTER FUNCTION <name> SET (FUEL = N, MEMORY = N) |
| 32 | if action == "SET" { |
| 33 | return alter_function_limits(state, identity, &name, parts); |
| 34 | } |
| 35 | |
| 36 | // ALTER FUNCTION <name> OWNER TO <new_owner> |
| 37 | if action != "OWNER" || parts.len() < 6 || !parts[4].eq_ignore_ascii_case("TO") { |
| 38 | return Err(sqlstate_error( |
| 39 | "42601", |
| 40 | "syntax: ALTER FUNCTION <name> OWNER TO <user> | SET (FUEL=N, MEMORY=N)", |
| 41 | )); |
| 42 | } |
| 43 | |
| 44 | let new_owner = parts[5].trim_end_matches(';').to_string(); |
| 45 | |
| 46 | let tenant_id = identity.tenant_id.as_u64(); |
| 47 | let catalog = state |
| 48 | .credentials |
| 49 | .catalog() |
| 50 | .as_ref() |
| 51 | .ok_or_else(|| sqlstate_error("XX000", "system catalog not available"))?; |
| 52 | |
| 53 | let mut func = catalog |
| 54 | .get_function(tenant_id, &name) |
| 55 | .map_err(|e| sqlstate_error("XX000", &e.to_string()))? |
| 56 | .ok_or_else(|| sqlstate_error("42883", &format!("function '{name}' does not exist")))?; |
| 57 | |
| 58 | let old_owner = func.owner.clone(); |
| 59 | func.owner = new_owner.clone(); |
| 60 | // Route through the same metadata-raft propose path every other |
| 61 | // parent-replicated ALTER uses. The applier's |
| 62 | // `owner::put_parent_owner` companion write rebinds the OWNERS |
| 63 | // table to the new owner cluster-wide — without this, an |
| 64 | // ALTER FUNCTION OWNER TO updated only the function row's |
| 65 | // in-band `owner` field and the OWNERS table still resolved the |
| 66 | // function to the previous owner, silently breaking permission |
| 67 | // transfer. |
| 68 | let entry = crate::control::catalog_entry::CatalogEntry::PutFunction(Box::new(func.clone())); |
| 69 | super::super::catalog_propose::propose_and_apply(state, &entry)?; |
| 70 | |
| 71 | state.audit_record( |
no test coverage detected