use std::{fmt::Display, iter::once};
use crate::{
ast::{Column, Expr},
value::Type,
};
#[derive(Debug, Clone)]
pub struct TableSchema {
pub readonly: bool,
pub name: String,
pub columns: Vec<ColumnSchema>,
pub checks: Vec<Expr>,
pub indexes: Vec<Index>,
pub primary_key: Vec<Expr>,
pub foreign_keys: Vec<ForeignKey>,
pub referenced_by: Vec<String>,
}
impl Display for TableSchema {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"CREATE{} TABLE {} ({});",
if self.readonly { " READONLY" } else { "" },
self.name,
self.columns
.iter()
.map(|c| format!(
"{} {}{}",
c.name,
c.ty,
if !c.nullable { " NOT NULL" } else { "" }
))
.chain(self.checks.iter().map(|c| format!("CHECK({c})")))
.chain(self.indexes.iter().map(|index| format!(
"{}KEY({})",
if index.unique { "UNIQUE " } else { "" },
index
.exprs
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",")
)))
.chain(once(format!(
"PRIMARY KEY({})",
self.primary_key
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",")
)))
.chain(self.foreign_keys.iter().map(|fk| fk.to_string()))
.chain(once(format!(
"REFERENCED BY({})",
self.referenced_by.clone().join(",")
)))
.collect::<Vec<String>>()
.join(",")
))
}
}
#[derive(Debug, Clone)]
pub struct Index {
pub exprs: Vec<Expr>,
pub unique: bool,
}
impl Index {
pub fn name(&self, table: &str) -> String {
format!(
"{table}.{}",
self.exprs
.iter()
.map(|e| format!("{e}"))
.collect::<Vec<_>>()
.join(",")
)
}
}
#[derive(Debug, Clone)]
pub struct ForeignKey {
pub lhs_exprs: Vec<Expr>,
pub rhs_table: String,
pub rhs_exprs: Vec<Expr>,
}
impl Display for ForeignKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"FOREIGN KEY({}) REFERENCES {}({})",
self.lhs_exprs
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(","),
self.rhs_table,
self.rhs_exprs
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(",")
))
}
}
#[derive(Debug, Clone)]
pub struct ColumnSchema {
pub name: String,
pub ty: Type,
pub nullable: bool,
}
pub const MATDB_TABLES: &str = "matdb_tables";
pub fn matdb_tables_schema() -> TableSchema {
TableSchema {
readonly: true,
name: MATDB_TABLES.to_string(),
columns: vec![
ColumnSchema {
name: "name".to_string(),
ty: Type::String,
nullable: false,
},
ColumnSchema {
name: "tree_id".to_string(),
ty: Type::Int,
nullable: false,
},
],
checks: vec![],
indexes: vec![],
primary_key: vec![Expr::Column(Column(
MATDB_TABLES.to_string(),
"name".to_string(),
))],
foreign_keys: vec![],
referenced_by: vec![],
}
}
pub const MATDB_SCHEMAS: &str = "matdb_schemas";
pub fn matdb_schemas_schema() -> TableSchema {
TableSchema {
readonly: true,
name: MATDB_SCHEMAS.to_string(),
columns: vec![
ColumnSchema {
name: "name".to_string(),
ty: Type::String,
nullable: false,
},
ColumnSchema {
name: "schema".to_string(),
ty: Type::String,
nullable: false,
},
],
checks: vec![],
indexes: vec![],
primary_key: vec![Expr::Column(Column(
MATDB_SCHEMAS.to_string(),
"name".to_string(),
))],
foreign_keys: vec![],
referenced_by: vec![],
}
}
pub const MATDB_REVS: &str = "matdb_revs";
pub fn matdb_revs_schema() -> TableSchema {
TableSchema {
readonly: true,
name: MATDB_REVS.to_string(),
columns: vec![
ColumnSchema {
name: "tx_id".to_string(),
ty: Type::Int,
nullable: false,
},
ColumnSchema {
name: "page_number".to_string(),
ty: Type::Int,
nullable: false,
},
],
checks: vec![],
indexes: vec![],
primary_key: vec![Expr::Column(Column(
MATDB_REVS.to_string(),
"tx_id".to_string(),
))],
foreign_keys: vec![],
referenced_by: vec![],
}
}