use std::collections::HashMap;
use crate::{DucklakeError, DucklakeResult, io};
mod arena;
mod load;
mod refs;
mod typedefs;
mod views;
use arena::{Arena, ArenaIdx};
pub(crate) use refs::{ColumnRef, SchemaRef, TableRef, ViewRef};
use typedefs::*;
pub(crate) use views::SchemaView;
#[derive(Debug, Clone)]
pub(crate) struct Catalog {
schema_arena: Arena<CatalogSchema>,
table_arena: Arena<CatalogTable>,
view_arena: Arena<CatalogView>,
schemas: HashMap<String, ArenaIdx>,
}
impl Catalog {
pub(crate) fn add_schema(
&mut self,
name: &str,
path: io::DucklakePath,
) -> DucklakeResult<SchemaRef> {
if self.schema(name).is_ok() {
return Err(DucklakeError::schema_already_exists(name));
}
let schema = CatalogSchema {
id: None,
name: name.to_string(),
tables: HashMap::new(),
views: HashMap::new(),
path,
};
let idx = self.schema_arena.push(schema, None);
self.schemas.insert(name.to_string(), idx);
Ok(idx.into())
}
}
impl Catalog {
#[allow(clippy::type_complexity)]
pub(crate) fn add_table(
&mut self,
table: crate::TableInfo,
path: io::DucklakePath,
next_column_id: Option<i64>,
) -> DucklakeResult<(
SchemaRef,
TableRef,
Vec<Vec<ColumnRef>>,
Option<Vec<ColumnRef>>,
)> {
if let Ok(table) = self.table(&table.name) {
return Err(DucklakeError::table_already_exists(table.name()));
}
if let Ok(view) = self.view(&table.name) {
return Err(DucklakeError::view_already_exists(view.name()));
}
let columns = CatalogColumns::from_schema(table.schema, next_column_id);
let partition = table
.partitioning
.map(|p| CatalogTablePartition::from_partition(p, &columns))
.transpose()?;
let catalog_table = CatalogTable {
id: None,
name: table.name.clone(),
columns,
partition: partition.clone(),
tags: table.tags,
path,
};
let column_idxs = catalog_table.columns.root_column_indices();
let table_idx = self.table_arena.push(catalog_table, None);
let mut schema = self.schema_mut(&table.name.schema)?;
let catalog_schema = schema.inner_mut();
catalog_schema
.tables
.insert(table.name.name.clone(), table_idx);
let column_refs = column_idxs
.into_iter()
.map(|idxs| {
idxs.into_iter()
.map(|idx| (table_idx, idx).into())
.collect()
})
.collect();
let partition_refs = partition.map(|p| {
p.columns
.iter()
.map(|col| (table_idx, col.column).into())
.collect()
});
Ok((schema.ref_(), table_idx.into(), column_refs, partition_refs))
}
}
impl Catalog {
pub(crate) fn add_view(
&mut self,
view: crate::ViewInfo,
) -> DucklakeResult<(SchemaRef, ViewRef)> {
if let Ok(view) = self.view(&view.name) {
return Err(DucklakeError::view_already_exists(view.name()));
}
if let Ok(table) = self.table(&view.name) {
return Err(DucklakeError::table_already_exists(table.name()));
}
let catalog_view = CatalogView {
id: None,
name: view.name.clone(),
sql: view.sql,
dialect: view.dialect,
column_aliases: view.column_aliases,
tags: view.tags,
};
let view_idx = self.view_arena.push(catalog_view, None);
let mut schema = self.schema_mut(&view.name.schema)?;
schema
.inner_mut()
.views
.insert(view.name.name.clone(), view_idx);
Ok((schema.ref_(), view_idx.into()))
}
}