use std::sync::Arc;
use radixdb_catalog::{CatalogGeneration, ObjectKind};
use radixdb_core::{Error, Result, Schema};
use crate::mvcc::{IndexDefinition, ViewDefinition};
#[derive(Debug)]
pub struct CatalogRuntime {
tables: Vec<CatalogRuntimeTable>,
views: Vec<ViewDefinition>,
}
impl CatalogRuntime {
pub fn new(
mut tables: Vec<CatalogRuntimeTable>,
mut views: Vec<ViewDefinition>,
) -> Result<Self> {
tables.sort_unstable_by(|left, right| {
left.schema
.table_name_lower
.cmp(&right.schema.table_name_lower)
});
views.sort_unstable_by(|left, right| left.name.cmp(&right.name));
if tables
.windows(2)
.any(|pair| pair[0].schema.table_name_lower == pair[1].schema.table_name_lower)
|| views.windows(2).any(|pair| pair[0].name == pair[1].name)
{
return Err(Error::internal(
"catalog runtime contains duplicate table or view names",
));
}
Ok(Self { tables, views })
}
pub(crate) fn into_parts(self) -> (Vec<CatalogRuntimeTable>, Vec<ViewDefinition>) {
(self.tables, self.views)
}
}
#[derive(Debug)]
pub struct CatalogRuntimeTable {
schema: Schema,
indexes: Vec<IndexDefinition>,
}
impl CatalogRuntimeTable {
pub fn new(schema: Schema, indexes: Vec<IndexDefinition>) -> Result<Self> {
if indexes
.iter()
.any(|index| !index.table_name.eq_ignore_ascii_case(&schema.table_name))
{
return Err(Error::internal(
"catalog runtime index belongs to another table",
));
}
Ok(Self { schema, indexes })
}
pub(crate) fn into_parts(self) -> (Schema, Vec<IndexDefinition>) {
(self.schema, self.indexes)
}
}
type BindCatalogRuntime = dyn Fn(&CatalogGeneration) -> Result<CatalogRuntime> + Send + Sync;
#[derive(Clone)]
pub struct CatalogRuntimeBinder(Arc<BindCatalogRuntime>);
impl CatalogRuntimeBinder {
pub fn new(
binder: impl Fn(&CatalogGeneration) -> Result<CatalogRuntime> + Send + Sync + 'static,
) -> Self {
Self(Arc::new(binder))
}
pub(crate) fn bind(&self, generation: &CatalogGeneration) -> Result<CatalogRuntime> {
(self.0)(generation)
}
}
impl std::fmt::Debug for CatalogRuntimeBinder {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("CatalogRuntimeBinder(..)")
}
}
#[doc(hidden)]
pub trait IntoCatalogRuntimeBinder {
fn into_catalog_runtime_binder(self) -> CatalogRuntimeBinder;
}
impl IntoCatalogRuntimeBinder for CatalogRuntimeBinder {
fn into_catalog_runtime_binder(self) -> CatalogRuntimeBinder {
self
}
}
impl<F> IntoCatalogRuntimeBinder for F
where
F: Fn(&CatalogGeneration) -> Result<CatalogRuntime> + Send + Sync + 'static,
{
fn into_catalog_runtime_binder(self) -> CatalogRuntimeBinder {
CatalogRuntimeBinder::new(self)
}
}
pub(super) fn missing_catalog_runtime_binder(
generation: &CatalogGeneration,
) -> Result<CatalogRuntime> {
if generation
.objects_of_kind(ObjectKind::Table)
.next()
.is_none()
&& generation
.objects_of_kind(ObjectKind::View)
.next()
.is_none()
{
return CatalogRuntime::new(Vec::new(), Vec::new());
}
Err(Error::NotSupported(
"persistent catalog recovery requires an executor catalog binder".to_owned(),
))
}
#[cfg(test)]
#[path = "catalog_runtime_test.rs"]
mod test_binder;
#[cfg(test)]
pub(super) use test_binder::bind_test_catalog_runtime;