use super::{CatalogIndexRow, Engine, RelationIdentity, StorageBackendError, StorageBackendResult};
pub(crate) fn is_virtual_system_schema(name: &str) -> bool {
matches!(name, "pg_catalog" | "information_schema" | "ag_catalog")
}
impl Engine {
pub fn list_catalog_indexes(&self) -> StorageBackendResult<Vec<CatalogIndexRow>> {
if let Some(snapshot) = self.query_catalog_snapshot.as_ref() {
let mut out = snapshot
.catalog_indexes
.values()
.cloned()
.collect::<Vec<_>>();
out.sort_by(|a, b| a.name.cmp(&b.name));
return Ok(out);
}
self.synchronize_catalog_registries()?;
let mut out: Vec<CatalogIndexRow> = self
.durable
.catalog_indexes
.read()
.values()
.cloned()
.collect();
out.sort_by(|a, b| a.name.cmp(&b.name));
Ok(out)
}
pub fn register_schema(&self, name: &str, if_not_exists: bool) -> StorageBackendResult<bool> {
self.with_implicit_storage_transaction(|engine| {
engine.register_schema_inner(name, if_not_exists)
})
}
fn register_schema_inner(&self, name: &str, if_not_exists: bool) -> StorageBackendResult<bool> {
self.synchronize_catalog_registries()?;
Self::validate_schema_name(name)?;
let mut schemas = self.durable.schemas.write();
if schemas.contains(name) || self.durable.graphs.read().contains_key(name) {
if if_not_exists {
return Ok(false);
}
return Err(StorageBackendError::Other(format!(
"schema `{name}` already exists"
)));
}
if let Some(catalog) = self.storage.catalog.as_ref() {
catalog.save_schema(name)?;
}
schemas.insert(name.to_string());
drop(schemas);
self.note_catalog_registry_changed();
Ok(true)
}
pub fn drop_schema(&self, name: &str) -> StorageBackendResult<bool> {
self.with_implicit_storage_transaction(|engine| engine.drop_schema_inner(name))
}
pub(crate) fn preflight_drop_schema(&self, name: &str) -> StorageBackendResult<bool> {
self.synchronize_catalog_registries()?;
if name == "public" || is_virtual_system_schema(name) {
return Err(StorageBackendError::Other(format!(
"schema `{name}` cannot be dropped"
)));
}
if !self.durable.schemas.read().contains(name) {
return Ok(false);
}
if !self.schema_is_empty(name) {
return Err(StorageBackendError::Other(format!(
"schema `{name}` is not empty"
)));
}
Ok(true)
}
fn drop_schema_inner(&self, name: &str) -> StorageBackendResult<bool> {
if !self.preflight_drop_schema(name)? {
return Ok(false);
}
let mut schemas = self.durable.schemas.write();
if let Some(catalog) = self.storage.catalog.as_ref() {
catalog.drop_schema(name)?;
}
let removed = schemas.remove(name);
drop(schemas);
if removed {
self.note_catalog_registry_changed();
}
Ok(removed)
}
pub fn has_schema(&self, name: &str) -> StorageBackendResult<bool> {
self.synchronize_catalog_registries()?;
Ok(self.durable.schemas.read().contains(name))
}
pub fn has_namespace(&self, name: &str) -> StorageBackendResult<bool> {
self.synchronize_catalog_registries()?;
Ok(is_virtual_system_schema(name)
|| self.durable.schemas.read().contains(name)
|| self.durable.graphs.read().contains_key(name))
}
pub(crate) fn validate_schema_name(name: &str) -> StorageBackendResult<()> {
if name.is_empty() {
return Err(StorageBackendError::Other(format!(
"invalid schema name `{name}`"
)));
}
if is_virtual_system_schema(name) {
return Err(StorageBackendError::Other(format!(
"schema name `{name}` is reserved"
)));
}
Ok(())
}
fn schema_is_empty(&self, schema: &str) -> bool {
!self
.storage
.tables
.read()
.keys()
.any(|relation| relation.schema == schema)
&& !self
.durable
.views
.read()
.keys()
.any(|relation| relation.schema == schema)
&& !self
.durable
.sequences
.read()
.keys()
.any(|relation| relation.schema == schema)
&& !self
.durable
.foreign_tables
.read()
.keys()
.any(|relation| relation.schema == schema)
&& !self.durable.sql_user_functions.read().keys().any(|name| {
RelationIdentity::from_legacy_name(name)
.map_or(true, |relation| relation.schema == schema)
})
}
pub fn list_schemas(&self) -> StorageBackendResult<Vec<String>> {
if let Some(snapshot) = self.query_catalog_snapshot.as_ref() {
return Ok(snapshot.schemas.iter().cloned().collect());
}
self.synchronize_catalog_registries()?;
Ok(self.durable.schemas.read().iter().cloned().collect())
}
pub fn tables_in_schema(&self, schema: &str) -> StorageBackendResult<Vec<String>> {
self.synchronize_table_catalog()?;
let mut out: Vec<String> = Vec::new();
for relation in self.storage.tables.read().keys() {
if relation.schema == schema {
out.push(relation.name.clone());
}
}
out.sort_unstable();
Ok(out)
}
pub fn list_sequences(&self) -> StorageBackendResult<Vec<String>> {
if let Some(snapshot) = self.query_catalog_snapshot.as_ref() {
let mut out = snapshot
.sequences
.keys()
.map(RelationIdentity::qualified_name)
.collect::<Vec<_>>();
out.sort_unstable();
return Ok(out);
}
self.refresh_sequences_from_catalog()?;
let mut out: Vec<String> = self
.durable
.sequences
.read()
.keys()
.map(RelationIdentity::qualified_name)
.collect();
out.sort_unstable();
Ok(out)
}
}