use crate::control::security::identity::AuthenticatedIdentity;
use crate::control::state::SharedState;
use super::super::super::catalog::propose_and_apply;
use super::super::super::result::{DdlError, DdlResult};
use super::super::auth_support::{require_tenant_admin, status};
pub fn alter_function(
state: &SharedState,
identity: &AuthenticatedIdentity,
parts: &[&str],
) -> Result<Vec<DdlResult>, DdlError> {
require_tenant_admin(identity, "alter functions")?;
if parts.len() < 4 {
return Err(DdlError {
sqlstate: "42601".to_string(),
message: "syntax: ALTER FUNCTION <name> OWNER TO <user> | SET (FUEL=N, MEMORY=N)"
.to_string(),
});
}
let name = parts[2].to_lowercase();
let action = parts[3].to_uppercase();
if action == "SET" {
return alter_function_limits(state, identity, &name, parts);
}
if action != "OWNER" || parts.len() < 6 || !parts[4].eq_ignore_ascii_case("TO") {
return Err(DdlError {
sqlstate: "42601".to_string(),
message: "syntax: ALTER FUNCTION <name> OWNER TO <user> | SET (FUEL=N, MEMORY=N)"
.to_string(),
});
}
let new_owner = parts[5].trim_end_matches(';').to_string();
let tenant_id = identity.tenant_id.as_u64();
let catalog = state.credentials.catalog();
let mut func = catalog
.get_function(tenant_id, &name)
.map_err(|e| DdlError {
sqlstate: "XX000".to_string(),
message: e.to_string(),
})?
.ok_or_else(|| DdlError {
sqlstate: "42883".to_string(),
message: format!("function '{name}' does not exist"),
})?;
let old_owner = func.owner.clone();
func.owner = new_owner.clone();
let entry = crate::control::catalog_entry::CatalogEntry::PutFunction(Box::new(func.clone()));
propose_and_apply(state, &entry)?;
state.audit_record(
crate::control::security::audit::AuditEvent::AdminAction,
Some(identity.tenant_id),
&identity.username,
&format!("ALTER FUNCTION {name} OWNER TO {new_owner} (was: {old_owner})"),
);
Ok(status("ALTER FUNCTION"))
}
fn alter_function_limits(
state: &SharedState,
identity: &AuthenticatedIdentity,
name: &str,
parts: &[&str],
) -> Result<Vec<DdlResult>, DdlError> {
let tenant_id = identity.tenant_id.as_u64();
let catalog = state.credentials.catalog();
let mut func = catalog
.get_function(tenant_id, name)
.map_err(|e| DdlError {
sqlstate: "XX000".to_string(),
message: e.to_string(),
})?
.ok_or_else(|| DdlError {
sqlstate: "42883".to_string(),
message: format!("function '{name}' does not exist"),
})?;
let rest = parts[4..].join(" ");
let rest = rest
.trim()
.trim_start_matches('(')
.trim_end_matches(')')
.trim_end_matches(';');
for part in rest.split(',') {
let kv: Vec<&str> = part.split('=').map(str::trim).collect();
if kv.len() != 2 {
continue;
}
match kv[0].to_uppercase().as_str() {
"FUEL" => {
if let Ok(v) = kv[1].parse::<u64>() {
func.wasm_fuel = v;
}
}
"MEMORY" => {
if let Ok(v) = kv[1].parse::<usize>() {
func.wasm_memory = v;
}
}
_ => {}
}
}
let entry = crate::control::catalog_entry::CatalogEntry::PutFunction(Box::new(func.clone()));
propose_and_apply(state, &entry)?;
state.audit_record(
crate::control::security::audit::AuditEvent::AdminAction,
Some(identity.tenant_id),
&identity.username,
&format!(
"ALTER FUNCTION {name} SET (FUEL={}, MEMORY={})",
func.wasm_fuel, func.wasm_memory
),
);
Ok(status("ALTER FUNCTION"))
}