uqa_sql/schema/namespaces/
removal.rs1use crate::{
10 catalog::{is_virtual_system_schema, security::BoundSchemaSecurity},
11 SQLError,
12};
13use std::collections::BTreeSet;
14
15pub trait EmptySchemaCatalog {
16 fn schema_registered(&self, name: &str) -> bool;
17 fn schema_is_empty(&self, name: &str) -> bool;
18}
19pub trait SchemaDropCatalog: EmptySchemaCatalog {
20 fn schema_security(&self, name: &str) -> Option<BoundSchemaSecurity>;
21 fn current_user_has_role_privileges(&self, role: &uqa_core::catalog_role::RoleIdentity)
22 -> bool;
23 fn schema_is_graph(&self, name: &str) -> Result<bool, String>;
24}
25pub enum BoundSchemaDrop {
26 Schema,
27 Graph,
28 Skipped(String),
29}
30
31pub fn bind_schema_drop_target(
32 catalog: &dyn SchemaDropCatalog,
33 name: &str,
34 if_exists: bool,
35) -> Result<BoundSchemaDrop, SQLError> {
36 let Some(security) = catalog.schema_security(name) else {
37 if if_exists {
38 return Ok(BoundSchemaDrop::Skipped(format!(
39 "schema \"{name}\" does not exist, skipping"
40 )));
41 }
42 return Err(SQLError::Routine {
43 sqlstate: "3F000".into(),
44 message: format!("schema \"{name}\" does not exist"),
45 });
46 };
47 if !catalog.current_user_has_role_privileges(&security.role_owner) {
48 return Err(SQLError::Routine {
49 sqlstate: "42501".into(),
50 message: format!("must be owner of schema {name}"),
51 });
52 }
53 if is_virtual_system_schema(name) {
54 return Err(SQLError::Routine {
55 sqlstate: "2BP01".into(),
56 message: format!("schema `{name}` cannot be dropped"),
57 });
58 }
59 if catalog
60 .schema_is_graph(name)
61 .map_err(|error| SQLError::Internal(format!("DROP SCHEMA: {error}")))?
62 {
63 Ok(BoundSchemaDrop::Graph)
64 } else {
65 Ok(BoundSchemaDrop::Schema)
66 }
67}
68
69pub fn validate_schema_drop_restrict(
70 catalog: &dyn SchemaDropCatalog,
71 schemas: &BTreeSet<String>,
72 graphs: &BTreeSet<String>,
73) -> Result<(), SQLError> {
74 let occupied = schemas
75 .iter()
76 .find(|name| !catalog.schema_is_empty(name))
77 .or_else(|| graphs.first());
78 if let Some(name) = occupied {
79 let single = schemas.len() + graphs.len() == 1;
80 let object = if single {
81 format!("schema {name}")
82 } else {
83 "desired object(s)".into()
84 };
85 return Err(SQLError::Routine {
86 sqlstate: "2BP01".into(),
87 message: format!(
88 "cannot drop {object} because other objects depend on {}",
89 if single { "it" } else { "them" }
90 ),
91 });
92 }
93 Ok(())
94}
95
96pub fn validate_empty_schema_drop(
97 catalog: &dyn EmptySchemaCatalog,
98 name: &str,
99) -> Result<bool, String> {
100 if is_virtual_system_schema(name) {
101 return Err(format!("schema `{name}` cannot be dropped"));
102 }
103 if !catalog.schema_registered(name) {
104 return Ok(false);
105 }
106 if !catalog.schema_is_empty(name) {
107 return Err(format!("schema `{name}` is not empty"));
108 }
109 Ok(true)
110}
111
112pub fn routine_name_occupies_schema(name: &str, schema: &str) -> bool {
113 uqa_core::RelationIdentity::from_legacy_name(name)
114 .map_or(true, |relation| relation.schema == schema)
115}