pub mod snapshot;
use xxhash_rust::xxh3::xxh3_64;
pub use self::snapshot::{
ColumnSnapshot, CustomDataTypeSnapshot, DataTypeSnapshot, ForeignKeySnapshot, IndexSnapshot,
OnDeleteSnapshot, TableSchemaSnapshot, WireSize,
};
use crate::dbms::foreign_fetcher::ForeignFetcher;
use crate::dbms::table::column_def::{ColumnDef, IndexDef};
use crate::dbms::table::{InsertRecord, TableRecord, UpdateRecord};
use crate::dbms::types::DataTypeKind;
use crate::memory::Encode;
use crate::prelude::{Sanitize, Validate};
pub type TableFingerprint = u64;
fn data_type_to_snapshot(kind: &DataTypeKind) -> DataTypeSnapshot {
match kind {
DataTypeKind::Blob => DataTypeSnapshot::Blob,
DataTypeKind::Boolean => DataTypeSnapshot::Boolean,
DataTypeKind::Date => DataTypeSnapshot::Date,
DataTypeKind::DateTime => DataTypeSnapshot::Datetime,
DataTypeKind::Decimal => DataTypeSnapshot::Decimal,
DataTypeKind::Int8 => DataTypeSnapshot::Int8,
DataTypeKind::Int16 => DataTypeSnapshot::Int16,
DataTypeKind::Int32 => DataTypeSnapshot::Int32,
DataTypeKind::Int64 => DataTypeSnapshot::Int64,
DataTypeKind::Json => DataTypeSnapshot::Json,
DataTypeKind::Text => DataTypeSnapshot::Text,
DataTypeKind::Uint8 => DataTypeSnapshot::Uint8,
DataTypeKind::Uint16 => DataTypeSnapshot::Uint16,
DataTypeKind::Uint32 => DataTypeSnapshot::Uint32,
DataTypeKind::Uint64 => DataTypeSnapshot::Uint64,
DataTypeKind::Uuid => DataTypeSnapshot::Uuid,
DataTypeKind::Custom { tag, wire_size } => {
DataTypeSnapshot::Custom(Box::new(CustomDataTypeSnapshot {
tag: (*tag).to_string(),
wire_size: *wire_size,
}))
}
}
}
pub trait TableSchema
where
Self: Encode + 'static,
{
type Record: TableRecord<Schema = Self>;
type Insert: InsertRecord<Schema = Self>;
type Update: UpdateRecord<Schema = Self>;
type ForeignFetcher: ForeignFetcher;
fn table_name() -> &'static str;
fn columns() -> &'static [ColumnDef];
fn primary_key() -> &'static str;
fn indexes() -> &'static [IndexDef] {
&[]
}
fn to_values(self) -> Vec<(ColumnDef, crate::dbms::value::Value)>;
fn sanitizer(column_name: &'static str) -> Option<Box<dyn Sanitize>>;
fn validator(column_name: &'static str) -> Option<Box<dyn Validate>>;
fn foreign_fetcher() -> Self::ForeignFetcher {
Default::default()
}
fn schema_snapshot() -> TableSchemaSnapshot {
let columns = Self::columns()
.iter()
.map(|c| ColumnSnapshot {
name: c.name.to_string(),
data_type: data_type_to_snapshot(&c.data_type),
nullable: c.nullable,
auto_increment: c.auto_increment,
unique: c.unique,
primary_key: c.primary_key,
foreign_key: c.foreign_key.as_ref().map(|fk| ForeignKeySnapshot {
table: fk.foreign_table.to_string(),
column: fk.foreign_column.to_string(),
on_delete: OnDeleteSnapshot::Restrict,
}),
default: c.default.map(|f| f()),
})
.collect();
let indexes = Self::indexes()
.iter()
.map(|idx| {
let cols = idx.columns();
let unique = match cols {
[single] => Self::columns()
.iter()
.find(|c| c.name == *single)
.is_some_and(|c| c.unique || c.primary_key),
_ => false,
};
IndexSnapshot {
columns: cols.iter().map(|c| (*c).to_string()).collect(),
unique,
}
})
.collect();
TableSchemaSnapshot {
version: TableSchemaSnapshot::latest_version(),
name: Self::table_name().to_string(),
primary_key: Self::primary_key().to_string(),
alignment: <Self as Encode>::ALIGNMENT as u32,
columns,
indexes,
}
}
fn fingerprint() -> TableFingerprint {
fingerprint_for_name(Self::table_name())
}
}
pub fn fingerprint_for_name(name: &str) -> TableFingerprint {
xxh3_64(name.as_bytes())
}