CREATE ROLE [IF NOT EXISTS] [INHERIT ]
(
state: &SharedState,
identity: &AuthenticatedIdentity,
parts: &[&str],
)
| 13 | |
| 14 | /// CREATE ROLE [IF NOT EXISTS] <name> [INHERIT <parent>] |
| 15 | pub fn create_role( |
| 16 | state: &SharedState, |
| 17 | identity: &AuthenticatedIdentity, |
| 18 | parts: &[&str], |
| 19 | ) -> PgWireResult<Vec<Response>> { |
| 20 | require_tenant_admin(identity, "create roles")?; |
| 21 | |
| 22 | let (if_not_exists, parts) = strip_if_not_exists(parts, 2); |
| 23 | |
| 24 | if parts.len() < 3 { |
| 25 | return Err(sqlstate_error( |
| 26 | "42601", |
| 27 | "syntax: CREATE ROLE [IF NOT EXISTS] <name> [INHERIT <parent>]", |
| 28 | )); |
| 29 | } |
| 30 | |
| 31 | let name = parts[2]; |
| 32 | |
| 33 | // `IF NOT EXISTS`: re-creating an existing role is a no-op success. |
| 34 | if if_not_exists && state.roles.get_role(name).is_some() { |
| 35 | return Ok(vec![Response::Execution(Tag::new("CREATE ROLE"))]); |
| 36 | } |
| 37 | |
| 38 | let parent = if parts.len() >= 5 && parts[3].eq_ignore_ascii_case("INHERIT") { |
| 39 | Some(parts[4]) |
| 40 | } else { |
| 41 | None |
| 42 | }; |
| 43 | |
| 44 | // Build the `StoredRole` on the proposer (runs the same |
| 45 | // validation as `create_role` but without touching state). |
| 46 | let stored = state |
| 47 | .roles |
| 48 | .prepare_role(name, identity.tenant_id, parent) |
| 49 | .map_err(|e| sqlstate_error("42710", &e.to_string()))?; |
| 50 | |
| 51 | let entry = crate::control::catalog_entry::CatalogEntry::PutRole(Box::new(stored.clone())); |
| 52 | let log_index = crate::control::metadata_proposer::propose_catalog_entry(state, &entry) |
| 53 | .map_err(|e| sqlstate_error("XX000", &format!("metadata propose: {e}")))?; |
| 54 | if log_index == 0 |
| 55 | && let Some(catalog) = state.credentials.catalog() |
| 56 | { |
| 57 | catalog |
| 58 | .put_role(&stored) |
| 59 | .map_err(|e| sqlstate_error("XX000", &format!("catalog write: {e}")))?; |
| 60 | state.roles.install_replicated_role(&stored); |
| 61 | } |
| 62 | |
| 63 | state.audit_record( |
| 64 | AuditEvent::PrivilegeChange, |
| 65 | Some(identity.tenant_id), |
| 66 | &identity.username, |
| 67 | &format!( |
| 68 | "created role '{name}'{}", |
| 69 | parent.map_or(String::new(), |p| format!(" inheriting from '{p}'")) |
| 70 | ), |
| 71 | ); |
| 72 |
no test coverage detected