1use std::cmp::Ordering;
2use std::hash::{Hash, Hasher};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6#[repr(u8)]
7pub enum TypeId {
8 Empty = 0,
9 Int = 1,
10 Float = 2,
11 Bool = 3,
12 Str = 4,
13 DateTime = 5,
14 Uuid = 6,
15 Bytes = 7,
16 Json = 8,
19}
20
21impl TypeId {
22 pub fn from_u8(v: u8) -> Option<Self> {
24 match v {
25 0 => Some(TypeId::Empty),
26 1 => Some(TypeId::Int),
27 2 => Some(TypeId::Float),
28 3 => Some(TypeId::Bool),
29 4 => Some(TypeId::Str),
30 5 => Some(TypeId::DateTime),
31 6 => Some(TypeId::Uuid),
32 7 => Some(TypeId::Bytes),
33 8 => Some(TypeId::Json),
34 _ => None,
35 }
36 }
37}
38
39#[derive(Debug, Clone)]
41pub enum Value {
42 Int(i64),
43 Float(f64),
44 Bool(bool),
45 Str(String),
46 DateTime(i64), Uuid([u8; 16]),
48 Bytes(Vec<u8>),
49 Json(Box<[u8]>),
52 Empty, }
54
55impl Value {
56 pub fn type_id(&self) -> TypeId {
57 match self {
58 Value::Int(_) => TypeId::Int,
59 Value::Float(_) => TypeId::Float,
60 Value::Bool(_) => TypeId::Bool,
61 Value::Str(_) => TypeId::Str,
62 Value::DateTime(_) => TypeId::DateTime,
63 Value::Uuid(_) => TypeId::Uuid,
64 Value::Bytes(_) => TypeId::Bytes,
65 Value::Json(_) => TypeId::Json,
66 Value::Empty => TypeId::Empty,
67 }
68 }
69
70 pub fn encoded_size(&self) -> usize {
72 match self {
73 Value::Int(_) => 8,
74 Value::Float(_) => 8,
75 Value::Bool(_) => 1,
76 Value::Str(s) => 4 + s.len(), Value::DateTime(_) => 8,
78 Value::Uuid(_) => 16,
79 Value::Bytes(b) => 4 + b.len(), Value::Json(b) => 4 + b.len(), Value::Empty => 0,
82 }
83 }
84
85 pub fn is_empty(&self) -> bool {
86 matches!(self, Value::Empty)
87 }
88
89 pub fn to_wire_string(&self) -> String {
96 match self {
97 Value::Int(n) => n.to_string(),
98 Value::Float(n) => format!("{n}"),
99 Value::Bool(b) => b.to_string(),
100 Value::Str(s) => s.clone(),
101 Value::DateTime(t) => format!("{t}"),
102 Value::Uuid(u) => format!(
103 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
104 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
105 u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]
106 ),
107 Value::Bytes(b) => format!("<{} bytes>", b.len()),
108 Value::Json(b) => crate::pj1::pj1_to_text(b).unwrap_or_else(|_| "null".into()),
113 Value::Empty => "null".into(),
114 }
115 }
116}
117
118impl PartialEq for Value {
127 fn eq(&self, other: &Self) -> bool {
128 match (self, other) {
129 (Value::Int(a), Value::Int(b)) => a == b,
130 (Value::Float(a), Value::Float(b)) => a.total_cmp(b) == Ordering::Equal,
131 (Value::Bool(a), Value::Bool(b)) => a == b,
132 (Value::Str(a), Value::Str(b)) => a == b,
133 (Value::DateTime(a), Value::DateTime(b)) => a == b,
134 (Value::Uuid(a), Value::Uuid(b)) => a == b,
135 (Value::Bytes(a), Value::Bytes(b)) => a == b,
136 (Value::Json(a), Value::Json(b)) => a == b,
139 (Value::Empty, Value::Empty) => true,
140 _ => false,
141 }
142 }
143}
144
145impl Eq for Value {}
146
147impl Hash for Value {
148 fn hash<H: Hasher>(&self, state: &mut H) {
149 std::mem::discriminant(self).hash(state);
152 match self {
153 Value::Int(v) => v.hash(state),
154 Value::Float(v) => v.to_bits().hash(state),
158 Value::Bool(v) => v.hash(state),
159 Value::Str(v) => v.hash(state),
160 Value::DateTime(v) => v.hash(state),
161 Value::Uuid(v) => v.hash(state),
162 Value::Bytes(v) => v.hash(state),
163 Value::Json(v) => v.hash(state),
166 Value::Empty => {} }
168 }
169}
170
171impl PartialOrd for Value {
172 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
173 Some(self.cmp(other))
174 }
175}
176
177impl Ord for Value {
178 fn cmp(&self, other: &Self) -> Ordering {
179 match (self, other) {
180 (Value::Int(a), Value::Int(b)) => a.cmp(b),
181 (Value::Float(a), Value::Float(b)) => a.total_cmp(b),
182 (Value::Int(a), Value::Float(b)) => (*a as f64).total_cmp(b),
188 (Value::Float(a), Value::Int(b)) => a.total_cmp(&(*b as f64)),
189 (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
190 (Value::Str(a), Value::Str(b)) => a.cmp(b),
191 (Value::DateTime(a), Value::DateTime(b)) => a.cmp(b),
192 (Value::DateTime(a), Value::Int(b)) => a.cmp(b),
202 (Value::Int(a), Value::DateTime(b)) => a.cmp(b),
203 (Value::Uuid(a), Value::Uuid(b)) => a.cmp(b),
204 (Value::Bytes(a), Value::Bytes(b)) => a.cmp(b),
205 (Value::Json(a), Value::Json(b)) => crate::pj1::pj1_cmp(a, b),
208 (Value::Empty, Value::Empty) => Ordering::Equal,
209 (Value::Empty, _) => Ordering::Less,
210 (_, Value::Empty) => Ordering::Greater,
211 _ => (self.type_id() as u8).cmp(&(other.type_id() as u8)),
212 }
213 }
214}
215
216#[derive(Debug, Clone)]
218pub struct ColumnDef {
219 pub name: String,
220 pub type_id: TypeId,
221 pub required: bool,
222 pub position: u16,
223}
224
225#[derive(Debug, Clone)]
227pub struct Schema {
228 pub table_name: String,
229 pub columns: Vec<ColumnDef>,
230}
231
232impl Schema {
233 pub fn column_count(&self) -> usize {
234 self.columns.len()
235 }
236
237 pub fn find_column(&self, name: &str) -> Option<&ColumnDef> {
238 self.columns.iter().find(|c| c.name == name)
239 }
240
241 pub fn column_index(&self, name: &str) -> Option<usize> {
242 self.columns.iter().position(|c| c.name == name)
243 }
244
245 pub fn null_bitmap_size(&self) -> usize {
247 self.columns.len().div_ceil(8)
248 }
249}
250
251pub fn is_fixed_size(type_id: TypeId) -> bool {
253 matches!(
254 type_id,
255 TypeId::Int | TypeId::Float | TypeId::Bool | TypeId::DateTime | TypeId::Uuid
256 )
257}
258
259pub fn fixed_size(type_id: TypeId) -> Option<usize> {
261 match type_id {
262 TypeId::Int => Some(8),
263 TypeId::Float => Some(8),
264 TypeId::Bool => Some(1),
265 TypeId::DateTime => Some(8),
266 TypeId::Uuid => Some(16),
267 _ => None,
268 }
269}
270
271pub type Row = Vec<Value>;
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
276pub struct RowId {
277 pub page_id: u32,
278 pub slot_index: u16,
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn test_value_to_wire_string() {
287 assert_eq!(Value::Int(42).to_wire_string(), "42");
288 assert_eq!(Value::Bool(true).to_wire_string(), "true");
289 assert_eq!(Value::Str("hi".into()).to_wire_string(), "hi");
290 assert_eq!(Value::Empty.to_wire_string(), "null");
292 assert_eq!(
294 Value::Uuid([
295 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
296 0x00, 0x00
297 ])
298 .to_wire_string(),
299 "550e8400-e29b-41d4-a716-446655440000"
300 );
301 assert_eq!(Value::Bytes(vec![1, 2, 3]).to_wire_string(), "<3 bytes>");
302 }
303
304 #[test]
305 fn test_value_type_id() {
306 assert_eq!(Value::Int(42).type_id(), TypeId::Int);
307 assert_eq!(Value::Str("hello".into()).type_id(), TypeId::Str);
308 assert_eq!(Value::Float(2.78).type_id(), TypeId::Float);
309 assert_eq!(Value::Bool(true).type_id(), TypeId::Bool);
310 assert_eq!(Value::Empty.type_id(), TypeId::Empty);
311 }
312
313 #[test]
314 fn test_value_encoded_size() {
315 assert_eq!(Value::Int(42).encoded_size(), 8);
316 assert_eq!(Value::Float(1.0).encoded_size(), 8);
317 assert_eq!(Value::Bool(true).encoded_size(), 1);
318 assert_eq!(Value::Str("hello".into()).encoded_size(), 4 + 5);
319 assert_eq!(Value::Empty.encoded_size(), 0);
320 }
321
322 #[test]
323 fn test_value_ordering() {
324 assert!(Value::Int(1) < Value::Int(2));
325 assert!(Value::Str("a".into()) < Value::Str("b".into()));
326 assert!(Value::Float(1.0) < Value::Float(2.0));
327 }
328
329 #[test]
330 fn test_datetime_value() {
331 let ts = Value::DateTime(1_700_000_000_000_000);
332 assert_eq!(ts.type_id(), TypeId::DateTime);
333 assert_eq!(ts.encoded_size(), 8);
334 }
335
336 #[test]
337 fn test_uuid_value() {
338 let uuid = Value::Uuid([0u8; 16]);
339 assert_eq!(uuid.type_id(), TypeId::Uuid);
340 assert_eq!(uuid.encoded_size(), 16);
341 }
342
343 #[test]
344 fn test_empty_is_less_than_values() {
345 assert!(Value::Empty < Value::Int(0));
346 assert!(Value::Empty < Value::Str("".into()));
347 }
348
349 #[test]
350 fn test_ord_int_vs_float() {
351 assert!(Value::Int(100) < Value::Float(175.5));
356 assert!(Value::Int(500) > Value::Float(450.0));
357 assert!(Value::Int(100) < Value::Float(100.5));
358 assert!(Value::Int(100) > Value::Float(99.9));
359 assert_eq!(Value::Int(100).cmp(&Value::Float(100.0)), Ordering::Equal);
361 assert_eq!(Value::Int(0).cmp(&Value::Float(0.0)), Ordering::Equal);
362 assert!(Value::Int(-10) < Value::Float(-5.5));
364 assert!(Value::Int(-1) > Value::Float(-1.5));
365 }
366
367 #[test]
368 fn test_ord_float_vs_int() {
369 assert!(Value::Float(175.5) > Value::Int(100));
370 assert!(Value::Float(450.0) < Value::Int(500));
371 assert!(Value::Float(100.5) > Value::Int(100));
372 assert!(Value::Float(99.9) < Value::Int(100));
373 assert_eq!(Value::Float(100.0).cmp(&Value::Int(100)), Ordering::Equal);
374 assert!(Value::Float(-5.5) > Value::Int(-10));
375 assert!(Value::Float(-1.5) < Value::Int(-1));
376 }
377
378 #[test]
379 fn test_ord_between_simulation() {
380 let lo = Value::Int(100);
383 let hi = Value::Int(500);
384 let prices = [29.0_f64, 175.5, 450.0, 1299.0];
385 let in_range: Vec<f64> = prices
386 .iter()
387 .copied()
388 .filter(|p| {
389 let v = Value::Float(*p);
390 v >= lo && v <= hi
391 })
392 .collect();
393 assert_eq!(in_range, vec![175.5, 450.0]);
394 }
395
396 #[test]
397 fn test_schema_column_lookup() {
398 let schema = Schema {
399 table_name: "test".into(),
400 columns: vec![
401 ColumnDef {
402 name: "a".into(),
403 type_id: TypeId::Int,
404 required: true,
405 position: 0,
406 },
407 ColumnDef {
408 name: "b".into(),
409 type_id: TypeId::Str,
410 required: false,
411 position: 1,
412 },
413 ],
414 };
415 assert_eq!(schema.column_index("a"), Some(0));
416 assert_eq!(schema.column_index("b"), Some(1));
417 assert_eq!(schema.column_index("c"), None);
418 assert_eq!(schema.null_bitmap_size(), 1);
419 }
420
421 #[test]
431 fn datetime_orders_against_int_by_microseconds_not_by_type_tag() {
432 use std::cmp::Ordering;
433 assert_eq!(Value::DateTime(100).cmp(&Value::Int(200)), Ordering::Less);
434 assert_eq!(
435 Value::DateTime(300).cmp(&Value::Int(200)),
436 Ordering::Greater
437 );
438 assert_eq!(Value::DateTime(200).cmp(&Value::Int(200)), Ordering::Equal);
439 assert_eq!(Value::Int(100).cmp(&Value::DateTime(200)), Ordering::Less);
440 assert_eq!(
441 Value::Int(300).cmp(&Value::DateTime(200)),
442 Ordering::Greater
443 );
444 assert_eq!(Value::Int(200).cmp(&Value::DateTime(200)), Ordering::Equal);
445
446 assert_eq!(Value::DateTime(-5).cmp(&Value::Int(5)), Ordering::Less);
449
450 assert_ne!(Value::DateTime(200), Value::Int(200));
452 }
453}