1use serde::{Deserialize, Serialize};
2
3use crate::constraint::TableConstraints;
4use crate::error::{MongrelError, Result};
5use crate::memtable::Value;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(tag = "kind", rename_all = "lowercase")]
11pub enum TypeId {
12 Bool,
13 Int8,
14 Int16,
15 Int32,
16 Int64,
17 UInt8,
18 UInt16,
19 UInt32,
20 UInt64,
21 Float32,
22 Float64,
23 TimestampNanos,
24 Date32,
25 Date64,
28 Time64,
30 Interval,
33 Bytes,
35 Embedding {
37 dim: u32,
38 },
39 Decimal128 {
42 precision: u8,
43 scale: i8,
44 },
45}
46
47impl TypeId {
48 pub const fn fixed_size(self) -> Option<usize> {
50 match self {
51 TypeId::Bool => Some(1),
52 TypeId::Int8 | TypeId::UInt8 => Some(1),
53 TypeId::Int16 | TypeId::UInt16 => Some(2),
54 TypeId::Int32 | TypeId::UInt32 | TypeId::Float32 | TypeId::Date32 => Some(4),
55 TypeId::Int64
56 | TypeId::UInt64
57 | TypeId::Float64
58 | TypeId::TimestampNanos
59 | TypeId::Date64
60 | TypeId::Time64 => Some(8),
61 TypeId::Bytes | TypeId::Embedding { .. } => None,
62 TypeId::Decimal128 { .. } => Some(16),
63 TypeId::Interval => Some(20), }
65 }
66}
67
68#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
70pub struct ColumnFlags {
71 bits: u32,
72}
73
74impl ColumnFlags {
75 pub const NULLABLE: u32 = 1 << 0;
76 pub const PRIMARY_KEY: u32 = 1 << 1;
77 pub const ENCRYPTED: u32 = 1 << 2;
78 pub const ENCRYPTED_INDEXABLE: u32 = 1 << 3;
81 pub const EMBEDDING_BINARY_QUANTIZED: u32 = 1 << 4;
83 pub const AUTO_INCREMENT: u32 = 1 << 5;
90
91 #[inline]
92 pub const fn empty() -> Self {
93 Self { bits: 0 }
94 }
95
96 #[inline]
97 pub const fn with(mut self, flag: u32) -> Self {
98 self.bits |= flag;
99 self
100 }
101
102 #[inline]
103 pub const fn without(mut self, flag: u32) -> Self {
104 self.bits &= !flag;
105 self
106 }
107
108 #[inline]
109 pub const fn contains(&self, flag: u32) -> bool {
110 self.bits & flag != 0
111 }
112
113 #[inline]
114 pub const fn bits(&self) -> u32 {
115 self.bits
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
121pub struct ColumnDef {
122 pub id: u16,
123 pub name: String,
124 pub ty: TypeId,
125 pub flags: ColumnFlags,
126}
127
128#[derive(Debug, Clone, Default, Serialize, Deserialize)]
130pub struct AlterColumn {
131 pub name: Option<String>,
132 pub ty: Option<TypeId>,
133 pub flags: Option<ColumnFlags>,
134}
135
136impl AlterColumn {
137 pub fn rename(name: impl Into<String>) -> Self {
138 Self {
139 name: Some(name.into()),
140 ty: None,
141 flags: None,
142 }
143 }
144
145 pub fn set_type(ty: TypeId) -> Self {
146 Self {
147 name: None,
148 ty: Some(ty),
149 flags: None,
150 }
151 }
152
153 pub fn set_flags(flags: ColumnFlags) -> Self {
154 Self {
155 name: None,
156 ty: None,
157 flags: Some(flags),
158 }
159 }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
165pub enum IndexKind {
166 Bitmap,
168 FmIndex,
170 Ann,
172 LearnedRange,
174 MinHash,
176 Sparse,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct IndexDef {
182 pub name: String,
183 pub column_id: u16,
184 pub kind: IndexKind,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub predicate: Option<String>,
190}
191
192#[derive(Debug, Clone, Default, Serialize, Deserialize)]
193pub struct Schema {
194 pub schema_id: u64,
195 pub columns: Vec<ColumnDef>,
196 pub indexes: Vec<IndexDef>,
197 #[serde(default)]
202 pub colocation: Vec<Vec<u16>>,
203 #[serde(default)]
208 pub constraints: TableConstraints,
209 #[serde(default)]
212 pub clustered: bool,
213}
214
215impl Schema {
216 pub fn column(&self, name: &str) -> Option<&ColumnDef> {
217 self.columns.iter().find(|c| c.name == name)
218 }
219
220 pub fn primary_key(&self) -> Option<&ColumnDef> {
221 self.columns
222 .iter()
223 .find(|c| c.flags.contains(ColumnFlags::PRIMARY_KEY))
224 }
225
226 pub fn validate_not_null(&self, columns: &[(u16, Value)]) -> Result<()> {
231 let at = |id: u16| columns.iter().find(|(c, _)| *c == id).map(|(_, v)| v);
234 for col in &self.columns {
235 if col.flags.contains(ColumnFlags::NULLABLE) {
236 continue;
237 }
238 if col.flags.contains(ColumnFlags::AUTO_INCREMENT) {
241 match at(col.id) {
242 None | Some(Value::Null) => continue,
243 Some(_) => {}
244 }
245 }
246 match at(col.id) {
247 None => {
248 return Err(MongrelError::InvalidArgument(format!(
249 "column '{}' ({}) is NOT NULL but was omitted",
250 col.name, col.id
251 )));
252 }
253 Some(Value::Null) => {
254 return Err(MongrelError::InvalidArgument(format!(
255 "column '{}' ({}) is NOT NULL but got NULL",
256 col.name, col.id
257 )));
258 }
259 Some(_) => {}
260 }
261 }
262 Ok(())
263 }
264
265 pub fn validate_auto_increment(&self) -> Result<()> {
269 let mut seen: Option<&ColumnDef> = None;
270 for col in &self.columns {
271 if !col.flags.contains(ColumnFlags::AUTO_INCREMENT) {
272 continue;
273 }
274 if let Some(prev) = seen {
275 return Err(MongrelError::Schema(format!(
276 "AUTO_INCREMENT may be set on at most one column; '{}' and '{}' both carry it",
277 prev.name, col.name
278 )));
279 }
280 if col.ty != TypeId::Int64 {
281 return Err(MongrelError::Schema(format!(
282 "AUTO_INCREMENT column '{}' must be Int64, is {:?}",
283 col.name, col.ty
284 )));
285 }
286 if !col.flags.contains(ColumnFlags::PRIMARY_KEY) {
287 return Err(MongrelError::Schema(format!(
288 "AUTO_INCREMENT column '{}' must also be the primary key",
289 col.name
290 )));
291 }
292 if col.flags.contains(ColumnFlags::NULLABLE) {
293 return Err(MongrelError::Schema(format!(
294 "AUTO_INCREMENT column '{}' must not be nullable",
295 col.name
296 )));
297 }
298 seen = Some(col);
299 }
300 Ok(())
301 }
302
303 pub fn auto_increment_column(&self) -> Option<&ColumnDef> {
305 self.columns
306 .iter()
307 .find(|c| c.flags.contains(ColumnFlags::AUTO_INCREMENT))
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn flag_composition() {
317 let f = ColumnFlags::empty()
318 .with(ColumnFlags::PRIMARY_KEY)
319 .with(ColumnFlags::ENCRYPTED_INDEXABLE);
320 assert!(f.contains(ColumnFlags::PRIMARY_KEY));
321 assert!(f.contains(ColumnFlags::ENCRYPTED_INDEXABLE));
322 assert!(!f.contains(ColumnFlags::ENCRYPTED));
323 }
324
325 #[test]
326 fn fixed_size() {
327 assert_eq!(TypeId::Int64.fixed_size(), Some(8));
328 assert_eq!(TypeId::Bytes.fixed_size(), None);
329 assert_eq!(TypeId::Embedding { dim: 768 }.fixed_size(), None);
330 }
331
332 fn col(id: u16, name: &str, ty: TypeId, flags: ColumnFlags) -> ColumnDef {
333 ColumnDef {
334 id,
335 name: name.into(),
336 ty,
337 flags,
338 }
339 }
340
341 #[test]
342 fn auto_increment_validation_accepts_int64_pk() {
343 let s = Schema {
344 schema_id: 1,
345 columns: vec![col(
346 0,
347 "id",
348 TypeId::Int64,
349 ColumnFlags::empty().with(ColumnFlags::PRIMARY_KEY | ColumnFlags::AUTO_INCREMENT),
350 )],
351 indexes: vec![],
352 colocation: vec![],
353 constraints: Default::default(),
354 clustered: false,
355 };
356 assert!(s.validate_auto_increment().is_ok());
357 assert_eq!(s.auto_increment_column().unwrap().id, 0);
358 }
359
360 #[test]
361 fn auto_increment_validation_rejects_non_pk() {
362 let s = Schema {
363 schema_id: 1,
364 columns: vec![
365 col(
366 0,
367 "id",
368 TypeId::Int64,
369 ColumnFlags::empty().with(ColumnFlags::PRIMARY_KEY),
370 ),
371 col(
372 1,
373 "seq",
374 TypeId::Int64,
375 ColumnFlags::empty().with(ColumnFlags::AUTO_INCREMENT),
376 ),
377 ],
378 indexes: vec![],
379 colocation: vec![],
380 constraints: Default::default(),
381 clustered: false,
382 };
383 assert!(s.validate_auto_increment().is_err());
384 }
385
386 #[test]
387 fn auto_increment_validation_rejects_non_int64() {
388 let s = Schema {
389 schema_id: 1,
390 columns: vec![col(
391 0,
392 "id",
393 TypeId::Bytes,
394 ColumnFlags::empty().with(ColumnFlags::PRIMARY_KEY | ColumnFlags::AUTO_INCREMENT),
395 )],
396 indexes: vec![],
397 colocation: vec![],
398 constraints: Default::default(),
399 clustered: false,
400 };
401 assert!(s.validate_auto_increment().is_err());
402 }
403
404 #[test]
405 fn auto_increment_validation_rejects_two() {
406 let s = Schema {
407 schema_id: 1,
408 columns: vec![
409 col(
410 0,
411 "id",
412 TypeId::Int64,
413 ColumnFlags::empty()
414 .with(ColumnFlags::PRIMARY_KEY | ColumnFlags::AUTO_INCREMENT),
415 ),
416 col(
417 1,
418 "id2",
419 TypeId::Int64,
420 ColumnFlags::empty().with(ColumnFlags::AUTO_INCREMENT),
421 ),
422 ],
423 indexes: vec![],
424 colocation: vec![],
425 constraints: Default::default(),
426 clustered: false,
427 };
428 assert!(s.validate_auto_increment().is_err());
429 }
430
431 #[test]
432 fn auto_increment_exempt_from_not_null_when_omitted() {
433 let s = Schema {
434 schema_id: 1,
435 columns: vec![
436 col(
437 0,
438 "id",
439 TypeId::Int64,
440 ColumnFlags::empty()
441 .with(ColumnFlags::PRIMARY_KEY | ColumnFlags::AUTO_INCREMENT),
442 ),
443 col(1, "name", TypeId::Bytes, ColumnFlags::empty()),
444 ],
445 indexes: vec![],
446 colocation: vec![],
447 constraints: Default::default(),
448 clustered: false,
449 };
450 let cols = vec![(1u16, Value::Bytes(b"x".to_vec()))];
452 assert!(s.validate_not_null(&cols).is_ok());
453 }
454}