1use std::collections::BTreeMap;
6use std::fmt::{self, Debug, Display};
7
8use kimberlite_store::TableId;
9
10#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct TableName(String);
17
18impl TableName {
19 pub fn new(name: impl Into<String>) -> Self {
21 Self(name.into())
22 }
23
24 pub fn as_str(&self) -> &str {
26 &self.0
27 }
28}
29
30impl Debug for TableName {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 write!(f, "TableName({:?})", self.0)
33 }
34}
35
36impl Display for TableName {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "{}", self.0)
39 }
40}
41
42impl From<&str> for TableName {
43 fn from(s: &str) -> Self {
44 Self::new(s)
45 }
46}
47
48impl From<String> for TableName {
49 fn from(s: String) -> Self {
50 Self::new(s)
51 }
52}
53
54#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub struct ColumnName(String);
57
58impl ColumnName {
59 pub fn new(name: impl Into<String>) -> Self {
61 Self(name.into())
62 }
63
64 pub fn as_str(&self) -> &str {
66 &self.0
67 }
68}
69
70impl Debug for ColumnName {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 write!(f, "ColumnName({:?})", self.0)
73 }
74}
75
76impl Display for ColumnName {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "{}", self.0)
79 }
80}
81
82impl From<&str> for ColumnName {
83 fn from(s: &str) -> Self {
84 Self::new(s)
85 }
86}
87
88impl From<String> for ColumnName {
89 fn from(s: String) -> Self {
90 Self::new(s)
91 }
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
100pub enum DataType {
101 TinyInt,
104 SmallInt,
106 Integer,
108 BigInt,
110
111 Real,
114 Decimal {
119 precision: u8,
121 scale: u8,
123 },
124
125 Text,
128
129 Bytes,
132
133 Boolean,
136
137 Date,
140 Time,
142 Timestamp,
144
145 Uuid,
148 Json,
150}
151
152impl Display for DataType {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 match self {
155 DataType::TinyInt => write!(f, "TINYINT"),
156 DataType::SmallInt => write!(f, "SMALLINT"),
157 DataType::Integer => write!(f, "INTEGER"),
158 DataType::BigInt => write!(f, "BIGINT"),
159 DataType::Real => write!(f, "REAL"),
160 DataType::Decimal { precision, scale } => write!(f, "DECIMAL({precision},{scale})"),
161 DataType::Text => write!(f, "TEXT"),
162 DataType::Bytes => write!(f, "BYTES"),
163 DataType::Boolean => write!(f, "BOOLEAN"),
164 DataType::Date => write!(f, "DATE"),
165 DataType::Time => write!(f, "TIME"),
166 DataType::Timestamp => write!(f, "TIMESTAMP"),
167 DataType::Uuid => write!(f, "UUID"),
168 DataType::Json => write!(f, "JSON"),
169 }
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct ColumnDef {
180 pub name: ColumnName,
182 pub data_type: DataType,
184 pub nullable: bool,
186}
187
188impl ColumnDef {
189 pub fn new(name: impl Into<ColumnName>, data_type: DataType) -> Self {
191 Self {
192 name: name.into(),
193 data_type,
194 nullable: true,
195 }
196 }
197
198 pub fn not_null(mut self) -> Self {
200 self.nullable = false;
201 self
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct IndexDef {
212 pub index_id: u64,
214 pub name: String,
216 pub columns: Vec<ColumnName>,
218}
219
220impl IndexDef {
221 pub fn new(index_id: u64, name: impl Into<String>, columns: Vec<ColumnName>) -> Self {
223 Self {
224 index_id,
225 name: name.into(),
226 columns,
227 }
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
237pub struct TableDef {
238 pub table_id: TableId,
240 pub columns: Vec<ColumnDef>,
242 pub primary_key: Vec<ColumnName>,
244 pub indexes: Vec<IndexDef>,
246}
247
248impl TableDef {
249 pub fn new(table_id: TableId, columns: Vec<ColumnDef>, primary_key: Vec<ColumnName>) -> Self {
251 for pk_col in &primary_key {
253 debug_assert!(
254 columns.iter().any(|c| &c.name == pk_col),
255 "primary key column '{pk_col}' not found in columns"
256 );
257 }
258
259 Self {
260 table_id,
261 columns,
262 primary_key,
263 indexes: Vec::new(),
264 }
265 }
266
267 pub fn with_index(mut self, index: IndexDef) -> Self {
269 self.indexes.push(index);
270 self
271 }
272
273 pub fn indexes(&self) -> &[IndexDef] {
275 &self.indexes
276 }
277
278 pub fn find_index_for_column(&self, column: &ColumnName) -> Option<&IndexDef> {
280 self.indexes
281 .iter()
282 .find(|idx| !idx.columns.is_empty() && &idx.columns[0] == column)
283 }
284
285 pub fn find_column(&self, name: &ColumnName) -> Option<(usize, &ColumnDef)> {
287 self.columns
288 .iter()
289 .enumerate()
290 .find(|(_, c)| &c.name == name)
291 }
292
293 pub fn is_primary_key(&self, name: &ColumnName) -> bool {
295 self.primary_key.contains(name)
296 }
297
298 pub fn primary_key_position(&self, name: &ColumnName) -> Option<usize> {
300 self.primary_key.iter().position(|pk| pk == name)
301 }
302
303 pub fn primary_key_indices(&self) -> Vec<usize> {
305 self.primary_key
306 .iter()
307 .filter_map(|pk| self.find_column(pk).map(|(idx, _)| idx))
308 .collect()
309 }
310}
311
312#[derive(Debug, Clone, Default)]
318pub struct Schema {
319 tables: BTreeMap<TableName, TableDef>,
320}
321
322impl Schema {
323 pub fn new() -> Self {
325 Self::default()
326 }
327
328 pub fn add_table(&mut self, name: impl Into<TableName>, def: TableDef) {
330 self.tables.insert(name.into(), def);
331 }
332
333 pub fn get_table(&self, name: &TableName) -> Option<&TableDef> {
335 self.tables.get(name)
336 }
337
338 pub fn table_names(&self) -> impl Iterator<Item = &TableName> {
340 self.tables.keys()
341 }
342
343 pub fn len(&self) -> usize {
345 self.tables.len()
346 }
347
348 pub fn is_empty(&self) -> bool {
350 self.tables.is_empty()
351 }
352}
353
354#[derive(Debug, Default)]
360pub struct SchemaBuilder {
361 schema: Schema,
362}
363
364impl SchemaBuilder {
365 pub fn new() -> Self {
367 Self::default()
368 }
369
370 pub fn table(
372 mut self,
373 name: impl Into<TableName>,
374 table_id: TableId,
375 columns: Vec<ColumnDef>,
376 primary_key: Vec<ColumnName>,
377 ) -> Self {
378 let def = TableDef::new(table_id, columns, primary_key);
379 self.schema.add_table(name, def);
380 self
381 }
382
383 pub fn build(self) -> Schema {
385 self.schema
386 }
387}