1#![warn(missing_docs)]
2
3use bytes::{BufMut, Bytes, BytesMut};
4use chrono::{DateTime, NaiveDate, NaiveTime, Utc};
5pub use rust_decimal::Decimal;
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use std::fmt;
9pub use uuid::Uuid;
10
11mod errors;
12pub mod layout;
13
14pub use errors::{DsError, DsErrorKind, DsResult};
15
16pub type PageId = u64;
18pub type Lsn = u64;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
23pub struct Redundancy(pub u8);
24
25impl Redundancy {
26 pub const TEMP: Redundancy = Redundancy(0);
28 pub const SINGLE: Redundancy = Redundancy(1);
30 pub const EDGE: Redundancy = Redundancy(2);
32 pub const SAFE: Redundancy = Redundancy(3);
34 pub const TRUTH: Redundancy = Redundancy(5);
36
37 pub fn from_u8(v: u8) -> Self {
39 Self(v)
40 }
41
42 pub fn replicas(&self) -> u8 {
44 if self.0 == 0 { 1 } else { self.0 }
45 }
46}
47
48#[derive(Debug, Clone)]
50pub struct Page {
51 pub id: PageId,
53 pub data: BytesMut,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct StorageConfig {
60 pub devices: Vec<DeviceConfig>,
62 pub page_size: usize,
64 pub wal_path: Option<String>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct DeviceConfig {
71 pub id: u32,
73 pub path: String,
75 pub medium: MediumType,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
81pub enum MediumType {
82 NVM,
84 NvmeSSD,
86 SataSSD,
88 HDD,
90}
91
92impl fmt::Display for MediumType {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 match self {
95 MediumType::NVM => write!(f, "NVM"),
96 MediumType::NvmeSSD => write!(f, "NVMe SSD"),
97 MediumType::SataSSD => write!(f, "SATA SSD"),
98 MediumType::HDD => write!(f, "HDD"),
99 }
100 }
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct PageInfo {
106 pub id: PageId,
108 pub medium: MediumType,
110 pub size: u32,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
116pub struct InternalId(pub u64);
117
118impl InternalId {
119 pub fn new(region_id: u8, shard_id: u16, sequence: u64) -> Self {
121 let id =
122 ((region_id as u64) << 56) | ((shard_id as u64) << 40) | (sequence & 0xFF_FFFF_FFFF);
123 Self(id)
124 }
125
126 pub fn region_id(&self) -> u8 {
128 (self.0 >> 56) as u8
129 }
130 pub fn shard_id(&self) -> u16 {
132 ((self.0 >> 40) & 0xFFFF) as u16
133 }
134 pub fn sequence(&self) -> u64 {
136 self.0 & 0xFF_FFFF_FFFF
137 }
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
142pub struct DsFile {
143 pub file_id: Uuid,
145 pub name: String,
147 pub size: u64,
149 pub mime_type: String,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
155pub struct Vector(pub Vec<f32>);
156
157impl Vector {
158 pub fn cosine_similarity(&self, other: &Self) -> f32 {
160 let a = &self.0;
161 let b = &other.0;
162 if a.len() != b.len() || a.is_empty() {
163 return 0.0;
164 }
165 let dot_product: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
166 let norm_a: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt();
167 let norm_b: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt();
168 if norm_a == 0.0 || norm_b == 0.0 {
169 return 0.0;
170 }
171 dot_product / (norm_a * norm_b)
172 }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
177pub enum FieldType {
178 Null,
180 Bool,
182 Int,
184 Float,
186 Text,
188 Bytes,
190 Uuid,
192 Decimal,
194 Date,
196 Time,
198 DateTime,
200 Vector,
202 File,
204 List,
206 Dict,
208 Object(u32),
210 Ext(String),
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
216pub struct FieldConstraint {
217 pub name: String,
219 pub field_type: FieldType,
221 pub nullable: bool,
223 pub default_value: Option<DsValue>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
229pub struct DsSchema {
230 pub id: u32,
232 pub name: String,
234 pub fields: BTreeMap<u32, FieldConstraint>,
236}
237
238impl DsSchema {
239 pub fn new(id: u32, name: &str) -> Self {
241 Self {
242 id,
243 name: name.to_string(),
244 fields: BTreeMap::new(),
245 }
246 }
247
248 pub fn add_field(&mut self, tag: u32, name: &str, field_type: FieldType, nullable: bool) {
250 self.fields.insert(
251 tag,
252 FieldConstraint {
253 name: name.to_string(),
254 field_type,
255 nullable,
256 default_value: None,
257 },
258 );
259 }
260
261 pub fn validate(&self, value: &DsValue) -> DsResult<()> {
263 match value {
264 DsValue::Object { schema_id, fields } => {
265 if *schema_id != self.id {
266 return Err(DsError::schema(
267 Some(self.name.clone()),
268 None,
269 format!(
270 "Schema ID mismatch: expected {}, got {}",
271 self.id, schema_id
272 ),
273 ));
274 }
275 for (tag, constraint) in &self.fields {
276 if fields.get(tag).is_none() && !constraint.nullable {
277 return Err(DsError::schema(
278 Some(self.name.clone()),
279 Some(constraint.name.clone()),
280 format!("Missing non-nullable field: {}", constraint.name),
281 ));
282 }
283 }
284 Ok(())
285 }
286 _ => Err(DsError::schema(
287 Some(self.name.clone()),
288 None,
289 "Value is not an object",
290 )),
291 }
292 }
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct TableInfo {
298 pub name: String,
300 pub columns: Vec<ColumnInfo>,
302 pub description: Option<String>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct ColumnInfo {
309 pub name: String,
311 pub data_type: String,
313 pub is_nullable: bool,
315 pub is_primary_key: bool,
317 pub is_enum: bool,
319 pub foreign_key: Option<ForeignKeyInfo>,
321 pub default: Option<String>,
323 pub description: Option<String>,
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct ForeignKeyInfo {
330 pub foreign_table: String,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct EnumInfo {
337 pub name: String,
339 pub variants: Vec<String>,
341}
342
343#[async_trait::async_trait]
345pub trait SchemaInspector: Send + Sync {
346 async fn introspect(&self, schema: Option<&str>) -> DsResult<(Vec<TableInfo>, Vec<EnumInfo>)>;
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
352pub enum DsValue {
353 Null,
355 Bool(bool),
357 Int(i64),
359 Float(f64),
361 Text(String),
363 Bytes(Bytes),
365 Binary(Bytes),
367 Uuid(Uuid),
369 Decimal(Decimal),
371 Date(NaiveDate),
373 Time(NaiveTime),
375 DateTime(DateTime<Utc>),
377 Vector(Vector),
379 File(DsFile),
381 List(Vec<DsValue>),
383 Dict(BTreeMap<String, DsValue>),
385 Object {
387 schema_id: u32,
389 fields: BTreeMap<u32, DsValue>,
391 },
392 Ext(String, Vec<u8>),
394}
395
396impl PartialOrd for DsValue {
397 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
398 match (self, other) {
399 (DsValue::Null, DsValue::Null) => Some(std::cmp::Ordering::Equal),
400 (DsValue::Bool(a), DsValue::Bool(b)) => a.partial_cmp(b),
401 (DsValue::Int(a), DsValue::Int(b)) => a.partial_cmp(b),
402 (DsValue::Float(a), DsValue::Float(b)) => a.partial_cmp(b),
403 (DsValue::Text(a), DsValue::Text(b)) => a.partial_cmp(b),
404 (DsValue::Bytes(a), DsValue::Bytes(b)) => a.partial_cmp(b),
405 (DsValue::Binary(a), DsValue::Binary(b)) => a.partial_cmp(b),
406 (DsValue::Uuid(a), DsValue::Uuid(b)) => a.partial_cmp(b),
407 (DsValue::Decimal(a), DsValue::Decimal(b)) => a.partial_cmp(b),
408 (DsValue::Date(a), DsValue::Date(b)) => a.partial_cmp(b),
409 (DsValue::Time(a), DsValue::Time(b)) => a.partial_cmp(b),
410 (DsValue::DateTime(a), DsValue::DateTime(b)) => a.partial_cmp(b),
411 (DsValue::Vector(a), DsValue::Vector(b)) => a.partial_cmp(b),
412 (DsValue::File(a), DsValue::File(b)) => a.partial_cmp(b),
413 (DsValue::List(a), DsValue::List(b)) => a.partial_cmp(b),
414 (DsValue::Dict(a), DsValue::Dict(b)) => a.partial_cmp(b),
415 (
416 DsValue::Object {
417 schema_id: s1,
418 fields: f1,
419 },
420 DsValue::Object {
421 schema_id: s2,
422 fields: f2,
423 },
424 ) => match s1.partial_cmp(s2) {
425 Some(std::cmp::Ordering::Equal) => f1.partial_cmp(f2),
426 ord => ord,
427 },
428 (DsValue::Ext(s1, v1), DsValue::Ext(s2, v2)) => match s1.partial_cmp(s2) {
429 Some(std::cmp::Ordering::Equal) => v1.partial_cmp(v2),
430 ord => ord,
431 },
432 _ => None,
433 }
434 }
435}
436
437impl fmt::Display for DsValue {
438 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439 match self {
440 DsValue::Null => write!(f, "NULL"),
441 DsValue::Bool(b) => write!(f, "{}", b),
442 DsValue::Int(i) => write!(f, "{}", i),
443 DsValue::Float(fl) => write!(f, "{}", fl),
444 DsValue::Text(s) => write!(f, "{}", s),
445 DsValue::Bytes(b) | DsValue::Binary(b) => write!(f, "0x{}", hex::encode(b)),
446 DsValue::Uuid(u) => write!(f, "{}", u),
447 DsValue::Decimal(d) => write!(f, "{}", d),
448 DsValue::Date(d) => write!(f, "{}", d),
449 DsValue::Time(t) => write!(f, "{}", t),
450 DsValue::DateTime(dt) => write!(f, "{}", dt),
451 DsValue::Vector(v) => write!(f, "Vector({:?})", v.0),
452 DsValue::Dict(d) => write!(f, "Dict({:?})", d),
453 DsValue::List(l) => write!(f, "List({:?})", l),
454 DsValue::Object { schema_id, fields } => {
455 write!(f, "Object(schema={}, fields={:?})", schema_id, fields)
456 }
457 DsValue::Ext(t, b) => write!(f, "Ext({}, 0x{})", t, hex::encode(b)),
458 DsValue::File(file) => write!(
459 f,
460 "File({}, name={}, size={})",
461 file.file_id, file.name, file.size
462 ),
463 }
464 }
465}
466
467pub trait ToValue {
469 fn to_value(&self) -> DsValue;
471}
472
473impl ToValue for DsValue {
474 fn to_value(&self) -> DsValue {
475 self.clone()
476 }
477}
478impl ToValue for i64 {
479 fn to_value(&self) -> DsValue {
480 DsValue::Int(*self)
481 }
482}
483impl ToValue for f32 {
484 fn to_value(&self) -> DsValue {
485 DsValue::Float(*self as f64)
486 }
487}
488impl ToValue for String {
489 fn to_value(&self) -> DsValue {
490 DsValue::Text(self.clone())
491 }
492}
493impl ToValue for &str {
494 fn to_value(&self) -> DsValue {
495 DsValue::Text(self.to_string())
496 }
497}
498impl ToValue for bool {
499 fn to_value(&self) -> DsValue {
500 DsValue::Bool(*self)
501 }
502}
503impl ToValue for Uuid {
504 fn to_value(&self) -> DsValue {
505 DsValue::Uuid(*self)
506 }
507}
508
509pub trait FromValue: Sized {
511 fn from_value(v: DsValue) -> DsResult<Self>;
513}