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