use crate::{SchemaError, ValueShapeValidator};
use sim_kernel::{Datum, Symbol};
use sim_relation_core::{
ColumnName, ConstraintName, DomainCatalog, DomainId, IndexName, RelationId, SchemaName,
TableName, ToRelationDatum, ViewName,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DefaultValue(pub Datum);
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GeneratedValue {
pub(crate) expression: Datum,
pub(crate) depends_on: Vec<ColumnName>,
}
impl GeneratedValue {
pub fn new(expression: Datum, depends_on: impl IntoIterator<Item = ColumnName>) -> Self {
Self {
expression,
depends_on: depends_on.into_iter().collect(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Column {
pub(crate) name: ColumnName,
pub(crate) domain: DomainId,
pub(crate) nullable: bool,
pub(crate) default: Option<DefaultValue>,
pub(crate) generated: Option<GeneratedValue>,
}
impl Column {
pub fn name(&self) -> &ColumnName {
&self.name
}
pub fn domain(&self) -> &DomainId {
&self.domain
}
pub const fn nullable(&self) -> bool {
self.nullable
}
pub const fn has_default(&self) -> bool {
self.default.is_some()
}
pub const fn is_generated(&self) -> bool {
self.generated.is_some()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PrimaryKey {
pub name: ConstraintName,
pub columns: Vec<ColumnName>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UniqueConstraint {
pub name: ConstraintName,
pub columns: Vec<ColumnName>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CheckConstraint {
pub name: ConstraintName,
pub expression: Datum,
pub columns: Vec<ColumnName>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ForeignKey {
pub name: ConstraintName,
pub columns: Vec<ColumnName>,
pub target_table: TableName,
pub target_columns: Vec<ColumnName>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Constraint {
Primary(PrimaryKey),
Unique(UniqueConstraint),
Check(CheckConstraint),
Foreign(ForeignKey),
}
impl Constraint {
pub(crate) fn name(&self) -> &ConstraintName {
match self {
Self::Primary(v) => &v.name,
Self::Unique(v) => &v.name,
Self::Check(v) => &v.name,
Self::Foreign(v) => &v.name,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Index {
pub name: IndexName,
pub columns: Vec<ColumnName>,
pub unique: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Table {
pub(crate) name: TableName,
pub(crate) columns: Vec<Column>,
pub(crate) constraints: Vec<Constraint>,
pub(crate) indexes: Vec<Index>,
}
impl Table {
pub fn name(&self) -> &TableName {
&self.name
}
pub fn columns(&self) -> &[Column] {
&self.columns
}
pub fn constraints(&self) -> &[Constraint] {
&self.constraints
}
pub fn indexes(&self) -> &[Index] {
&self.indexes
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct View {
pub name: ViewName,
pub query: Datum,
pub table_dependencies: Vec<TableName>,
pub view_dependencies: Vec<ViewName>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Schema {
pub(crate) name: SchemaName,
pub(crate) tables: Vec<Table>,
pub(crate) views: Vec<View>,
}
impl Schema {
pub fn new(
name: SchemaName,
tables: impl IntoIterator<Item = Table>,
views: impl IntoIterator<Item = View>,
domains: &DomainCatalog,
validator: &impl ValueShapeValidator,
) -> Result<Self, SchemaError> {
crate::validation::validate(
name,
tables.into_iter().collect(),
views.into_iter().collect(),
domains,
validator,
)
}
pub fn name(&self) -> &SchemaName {
&self.name
}
pub fn tables(&self) -> &[Table] {
&self.tables
}
pub fn views(&self) -> &[View] {
&self.views
}
pub fn id(&self) -> Result<RelationId, sim_kernel::Error> {
RelationId::of(self)
}
}
fn sym(name: &str, value: Symbol) -> (Symbol, Datum) {
(Symbol::new(name), Datum::Symbol(value))
}
fn node(tag: &str, fields: Vec<(Symbol, Datum)>) -> Datum {
Datum::Node {
tag: Symbol::qualified("relation-schema", tag),
fields,
}
}
fn names<T>(values: &[T], f: impl Fn(&T) -> Symbol) -> Datum {
Datum::Vector(values.iter().map(|v| Datum::Symbol(f(v))).collect())
}
impl ToRelationDatum for Column {
fn to_datum(&self) -> Datum {
node(
"column",
vec![
sym("name", self.name.symbol().clone()),
sym("domain", self.domain.symbol().clone()),
(Symbol::new("nullable"), Datum::Bool(self.nullable)),
(
Symbol::new("default"),
self.default.as_ref().map_or(Datum::Nil, |v| v.0.clone()),
),
(
Symbol::new("generated"),
self.generated.as_ref().map_or(Datum::Nil, |v| {
node(
"generated",
vec![
(Symbol::new("expression"), v.expression.clone()),
(
Symbol::new("depends-on"),
names(&v.depends_on, |n| n.symbol().clone()),
),
],
)
}),
),
],
)
}
}
impl ToRelationDatum for Constraint {
fn to_datum(&self) -> Datum {
match self {
Self::Primary(v) => node(
"primary",
vec![
sym("name", v.name.symbol().clone()),
(
Symbol::new("columns"),
names(&v.columns, |n| n.symbol().clone()),
),
],
),
Self::Unique(v) => node(
"unique",
vec![
sym("name", v.name.symbol().clone()),
(
Symbol::new("columns"),
names(&v.columns, |n| n.symbol().clone()),
),
],
),
Self::Check(v) => node(
"check",
vec![
sym("name", v.name.symbol().clone()),
(Symbol::new("expression"), v.expression.clone()),
(
Symbol::new("columns"),
names(&v.columns, |n| n.symbol().clone()),
),
],
),
Self::Foreign(v) => node(
"foreign",
vec![
sym("name", v.name.symbol().clone()),
(
Symbol::new("columns"),
names(&v.columns, |n| n.symbol().clone()),
),
sym("target-table", v.target_table.symbol().clone()),
(
Symbol::new("target-columns"),
names(&v.target_columns, |n| n.symbol().clone()),
),
],
),
}
}
}
impl ToRelationDatum for Index {
fn to_datum(&self) -> Datum {
node(
"index",
vec![
sym("name", self.name.symbol().clone()),
(
Symbol::new("columns"),
names(&self.columns, |n| n.symbol().clone()),
),
(Symbol::new("unique"), Datum::Bool(self.unique)),
],
)
}
}
impl ToRelationDatum for Table {
fn to_datum(&self) -> Datum {
node(
"table",
vec![
sym("name", self.name.symbol().clone()),
(
Symbol::new("columns"),
Datum::Vector(self.columns.iter().map(ToRelationDatum::to_datum).collect()),
),
(
Symbol::new("constraints"),
Datum::Vector(
self.constraints
.iter()
.map(ToRelationDatum::to_datum)
.collect(),
),
),
(
Symbol::new("indexes"),
Datum::Vector(self.indexes.iter().map(ToRelationDatum::to_datum).collect()),
),
],
)
}
}
impl ToRelationDatum for View {
fn to_datum(&self) -> Datum {
node(
"view",
vec![
sym("name", self.name.symbol().clone()),
(Symbol::new("query"), self.query.clone()),
(
Symbol::new("tables"),
names(&self.table_dependencies, |n| n.symbol().clone()),
),
(
Symbol::new("views"),
names(&self.view_dependencies, |n| n.symbol().clone()),
),
],
)
}
}
impl ToRelationDatum for Schema {
fn to_datum(&self) -> Datum {
node(
"logical-schema",
vec![
sym("name", self.name.symbol().clone()),
(
Symbol::new("tables"),
Datum::Vector(self.tables.iter().map(ToRelationDatum::to_datum).collect()),
),
(
Symbol::new("views"),
Datum::Vector(self.views.iter().map(ToRelationDatum::to_datum).collect()),
),
],
)
}
}