Skip to main content

wasm_dbms_api/dbms/
value.rs

1mod discriminant;
2
3use std::borrow::Cow;
4use std::str::FromStr;
5use std::sync::OnceLock;
6
7use serde::{Deserialize, Serialize};
8
9use super::types;
10use crate::memory::{
11    DEFAULT_ALIGNMENT, DataSize, DecodeError, Encode, MSize, MemoryError, MemoryResult, PageOffset,
12};
13
14/// A generic wrapper enum to hold any DBMS value.
15#[cfg_attr(feature = "candid", derive(candid::CandidType))]
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
17pub enum Value {
18    Blob(types::Blob),
19    Boolean(types::Boolean),
20    Date(types::Date),
21    DateTime(types::DateTime),
22    Decimal(types::Decimal),
23    Int8(types::Int8),
24    Int16(types::Int16),
25    Int32(types::Int32),
26    Int64(types::Int64),
27    Json(types::Json),
28    Null,
29    Text(types::Text),
30    Uint8(types::Uint8),
31    Uint16(types::Uint16),
32    Uint32(types::Uint32),
33    Uint64(types::Uint64),
34    Uuid(types::Uuid),
35    Custom(crate::dbms::custom_value::CustomValue),
36}
37
38impl FromStr for Value {
39    type Err = ();
40
41    fn from_str(s: &str) -> Result<Self, Self::Err> {
42        Ok(Self::Text(s.into()))
43    }
44}
45
46// macro rules for implementing From trait for Value enum variants
47macro_rules! impl_conv_for_value {
48    ($variant:ident, $ty:ty, $name:ident, $test_name:ident) => {
49        impl From<$ty> for Value {
50            fn from(value: $ty) -> Self {
51                Value::$variant(value)
52            }
53        }
54
55        impl Value {
56            /// Attempts to extract a reference to the inner value if it matches the variant.
57            pub fn $name(&self) -> Option<&$ty> {
58                if let Value::$variant(v) = self {
59                    Some(v)
60                } else {
61                    None
62                }
63            }
64        }
65
66        #[cfg(test)]
67        mod $test_name {
68            use super::*;
69
70            #[test]
71            fn test_value_conversion() {
72                let value_instance: $ty = Default::default();
73                let value: Value = value_instance.clone().into();
74                assert_eq!(value.$name(), Some(&value_instance));
75            }
76        }
77    };
78}
79
80macro_rules! value_from_primitive {
81    ($variant:ident, $primitive:ty, $test_name:ident) => {
82        value_from_primitive!($variant, $primitive, $test_name, Default::default());
83    };
84
85    ($variant:ident, $primitive:ty, $test_name:ident, $default_value:expr) => {
86        impl From<$primitive> for Value {
87            fn from(value: $primitive) -> Self {
88                Value::$variant($crate::prelude::$variant(value.into()))
89            }
90        }
91
92        #[cfg(test)]
93        mod $test_name {
94            use super::*;
95
96            #[test]
97            fn test_value_from_primitive() {
98                let primitive_value: $primitive = $default_value;
99                if let Value::$variant(inner_value) = Value::from(primitive_value.clone()) {
100                    assert_eq!(inner_value.0, primitive_value);
101                } else {
102                    panic!("Value variant does not match");
103                }
104            }
105        }
106    };
107}
108
109// implement conversions for all Value variants
110impl_conv_for_value!(Blob, types::Blob, as_blob, tests_blob);
111impl_conv_for_value!(Boolean, types::Boolean, as_boolean, tests_boolean);
112impl_conv_for_value!(Date, types::Date, as_date, tests_date);
113impl_conv_for_value!(DateTime, types::DateTime, as_datetime, tests_datetime);
114impl_conv_for_value!(Decimal, types::Decimal, as_decimal, tests_decimal);
115impl_conv_for_value!(Int8, types::Int8, as_int8, tests_int8);
116impl_conv_for_value!(Int16, types::Int16, as_int16, tests_int16);
117impl_conv_for_value!(Int32, types::Int32, as_int32, tests_int32);
118impl_conv_for_value!(Int64, types::Int64, as_int64, tests_int64);
119impl_conv_for_value!(Json, types::Json, as_json, tests_json);
120impl_conv_for_value!(Text, types::Text, as_text, tests_text);
121impl_conv_for_value!(Uint8, types::Uint8, as_uint8, tests_uint8);
122impl_conv_for_value!(Uint16, types::Uint16, as_uint16, tests_uint16);
123impl_conv_for_value!(Uint32, types::Uint32, as_uint32, tests_uint32);
124impl_conv_for_value!(Uint64, types::Uint64, as_uint64, tests_uint64);
125impl_conv_for_value!(Uuid, types::Uuid, as_uuid, tests_uuid);
126
127// from inner values of types
128value_from_primitive!(Blob, &[u8], tests_blob_primitive_slice);
129value_from_primitive!(Blob, Vec<u8>, tests_blob_primitive);
130value_from_primitive!(Boolean, bool, tests_boolean_primitive);
131value_from_primitive!(Decimal, rust_decimal::Decimal, tests_decimal_primitive);
132value_from_primitive!(Int8, i8, tests_int8_primitive);
133value_from_primitive!(Int16, i16, tests_int16_primitive);
134value_from_primitive!(Int32, i32, tests_int32_primitive);
135value_from_primitive!(Int64, i64, tests_int64_primitive);
136value_from_primitive!(Uint8, u8, tests_uint8_primitive);
137value_from_primitive!(Uint16, u16, tests_uint16_primitive);
138value_from_primitive!(Uint32, u32, tests_uint32_primitive);
139value_from_primitive!(Uint64, u64, tests_uint64_primitive);
140value_from_primitive!(Text, String, tests_text_primitive_string);
141value_from_primitive!(Text, &str, tests_text_primitive_str);
142value_from_primitive!(Uuid, uuid::Uuid, tests_uuid_primitive);
143
144impl Value {
145    /// Checks if the value is [`Value::Null`].
146    pub fn is_null(&self) -> bool {
147        matches!(self, Value::Null)
148    }
149
150    /// Returns the type name of the value as a string.
151    pub fn type_name(&self) -> &'static str {
152        match self {
153            Value::Blob(_) => "Blob",
154            Value::Boolean(_) => "Boolean",
155            Value::Date(_) => "Date",
156            Value::DateTime(_) => "DateTime",
157            Value::Decimal(_) => "Decimal",
158            Value::Int8(_) => "Int8",
159            Value::Int16(_) => "Int16",
160            Value::Int32(_) => "Int32",
161            Value::Int64(_) => "Int64",
162            Value::Json(_) => "Json",
163            Value::Null => "Null",
164            Value::Text(_) => "Text",
165            Value::Uint8(_) => "Uint8",
166            Value::Uint16(_) => "Uint16",
167            Value::Uint32(_) => "Uint32",
168            Value::Uint64(_) => "Uint64",
169            Value::Uuid(_) => "Uuid",
170            Value::Custom(cv) => {
171                // Cache custom type names to avoid repeated allocations.
172                // The number of unique type tags is bounded at compile time,
173                // so the map grows to a fixed size. A maximum of 64 entries
174                // is enforced as a safety guard against unbounded memory usage
175                // on the IC, where heap is a scarce resource.
176                const MAX_CACHE_ENTRIES: usize = 64;
177                static CACHE: OnceLock<
178                    std::sync::Mutex<std::collections::HashMap<String, &'static str>>,
179                > = OnceLock::new();
180                let cache =
181                    CACHE.get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
182                let mut map = cache.lock().unwrap_or_else(|e| e.into_inner());
183                if map.len() >= MAX_CACHE_ENTRIES && !map.contains_key(&cv.type_tag) {
184                    return "Custom(?)";
185                }
186                map.entry(cv.type_tag.clone()).or_insert_with(|| {
187                    let s = format!("Custom({})", cv.type_tag);
188                    s.leak()
189                })
190            }
191        }
192    }
193
194    /// Returns reference to the inner [`CustomValue`](crate::dbms::custom_value::CustomValue) if this is a `Custom` variant.
195    pub fn as_custom(&self) -> Option<&crate::dbms::custom_value::CustomValue> {
196        match self {
197            Value::Custom(v) => Some(v),
198            _ => None,
199        }
200    }
201
202    /// Attempts to decode a `Custom` variant into a concrete [`CustomDataType`](crate::dbms::types::CustomDataType).
203    ///
204    /// Returns `None` if this is not a `Custom` variant, the type tag doesn't
205    /// match, or decoding fails.
206    pub fn as_custom_type<T: crate::dbms::types::CustomDataType>(&self) -> Option<T> {
207        self.as_custom()
208            .filter(|cv| cv.type_tag == T::TYPE_TAG)
209            .and_then(|cv| T::decode(std::borrow::Cow::Borrowed(&cv.encoded)).ok())
210    }
211}
212
213/// Encodes a [`Value`] as `[discriminant: u8] + [inner_type.encode()]`.
214///
215/// For `Null`, only the discriminant byte is written.
216/// For `Custom`, the encoding is `[discriminant] + [tag_len: u16 LE] + [tag_bytes] + [data_len: u16 LE] + [encoded_bytes]`.
217///
218/// [`Encode::size`] saturates at [`MSize::MAX`] when a value does not fit the
219/// 2-byte length prefixes; storage rejects such values before encoding.
220impl Encode for Value {
221    const SIZE: DataSize = DataSize::Dynamic;
222    const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
223
224    fn encode(&'_ self) -> Cow<'_, [u8]> {
225        match self {
226            Value::Blob(v) => encode_with_discriminant(discriminant::BLOB, v.encode()),
227            Value::Boolean(v) => encode_with_discriminant(discriminant::BOOLEAN, v.encode()),
228            Value::Date(v) => encode_with_discriminant(discriminant::DATE, v.encode()),
229            Value::DateTime(v) => encode_with_discriminant(discriminant::DATE_TIME, v.encode()),
230            Value::Decimal(v) => encode_with_discriminant(discriminant::DECIMAL, v.encode()),
231            Value::Int8(v) => encode_with_discriminant(discriminant::INT8, v.encode()),
232            Value::Int16(v) => encode_with_discriminant(discriminant::INT16, v.encode()),
233            Value::Int32(v) => encode_with_discriminant(discriminant::INT32, v.encode()),
234            Value::Int64(v) => encode_with_discriminant(discriminant::INT64, v.encode()),
235            Value::Json(v) => encode_with_discriminant(discriminant::JSON, v.encode()),
236            Value::Null => Cow::Owned(vec![discriminant::NULL]),
237            Value::Text(v) => encode_with_discriminant(discriminant::TEXT, v.encode()),
238            Value::Uint8(v) => encode_with_discriminant(discriminant::UINT8, v.encode()),
239            Value::Uint16(v) => encode_with_discriminant(discriminant::UINT16, v.encode()),
240            Value::Uint32(v) => encode_with_discriminant(discriminant::UINT32, v.encode()),
241            Value::Uint64(v) => encode_with_discriminant(discriminant::UINT64, v.encode()),
242            Value::Uuid(v) => encode_with_discriminant(discriminant::UUID, v.encode()),
243            Value::Custom(cv) => {
244                let tag_bytes = cv.type_tag.as_bytes();
245                let total = 1 + 2 + tag_bytes.len() + 2 + cv.encoded.len();
246                let mut buf = Vec::with_capacity(total);
247                buf.push(discriminant::CUSTOM);
248                buf.extend_from_slice(&crate::memory::length_prefix(tag_bytes.len()));
249                buf.extend_from_slice(tag_bytes);
250                buf.extend_from_slice(&crate::memory::length_prefix(cv.encoded.len()));
251                buf.extend_from_slice(&cv.encoded);
252                Cow::Owned(buf)
253            }
254        }
255    }
256
257    fn decode(data: Cow<[u8]>) -> MemoryResult<Self> {
258        if data.is_empty() {
259            return Err(MemoryError::DecodeError(DecodeError::TooShort));
260        }
261
262        let disc = data[0];
263        let rest = Cow::Owned(data[1..].to_vec());
264
265        match disc {
266            discriminant::BLOB => types::Blob::decode(rest).map(Value::Blob),
267            discriminant::BOOLEAN => types::Boolean::decode(rest).map(Value::Boolean),
268            discriminant::DATE => types::Date::decode(rest).map(Value::Date),
269            discriminant::DATE_TIME => types::DateTime::decode(rest).map(Value::DateTime),
270            discriminant::DECIMAL => types::Decimal::decode(rest).map(Value::Decimal),
271            discriminant::INT8 => types::Int8::decode(rest).map(Value::Int8),
272            discriminant::INT16 => types::Int16::decode(rest).map(Value::Int16),
273            discriminant::INT32 => types::Int32::decode(rest).map(Value::Int32),
274            discriminant::INT64 => types::Int64::decode(rest).map(Value::Int64),
275            discriminant::JSON => types::Json::decode(rest).map(Value::Json),
276            discriminant::NULL => Ok(Value::Null),
277            discriminant::TEXT => types::Text::decode(rest).map(Value::Text),
278            discriminant::UINT8 => types::Uint8::decode(rest).map(Value::Uint8),
279            discriminant::UINT16 => types::Uint16::decode(rest).map(Value::Uint16),
280            discriminant::UINT32 => types::Uint32::decode(rest).map(Value::Uint32),
281            discriminant::UINT64 => types::Uint64::decode(rest).map(Value::Uint64),
282            discriminant::UUID => types::Uuid::decode(rest).map(Value::Uuid),
283            discriminant::CUSTOM => decode_custom_value(&data[1..]),
284            other => Err(MemoryError::DecodeError(DecodeError::InvalidDiscriminant(
285                other,
286            ))),
287        }
288    }
289
290    fn size(&self) -> MSize {
291        let inner = match self {
292            Value::Blob(v) => Encode::size(v),
293            Value::Boolean(v) => Encode::size(v),
294            Value::Date(v) => Encode::size(v),
295            Value::DateTime(v) => Encode::size(v),
296            Value::Decimal(v) => Encode::size(v),
297            Value::Int8(v) => Encode::size(v),
298            Value::Int16(v) => Encode::size(v),
299            Value::Int32(v) => Encode::size(v),
300            Value::Int64(v) => Encode::size(v),
301            Value::Json(v) => Encode::size(v),
302            Value::Null => 0,
303            Value::Text(v) => Encode::size(v),
304            Value::Uint8(v) => Encode::size(v),
305            Value::Uint16(v) => Encode::size(v),
306            Value::Uint32(v) => Encode::size(v),
307            Value::Uint64(v) => Encode::size(v),
308            Value::Uuid(v) => Encode::size(v),
309            Value::Custom(cv) => {
310                // tag_len(2) + tag_bytes + data_len(2) + encoded_bytes
311                crate::memory::saturating_size(
312                    2,
313                    cv.type_tag
314                        .len()
315                        .saturating_add(2)
316                        .saturating_add(cv.encoded.len()),
317                )
318            }
319        };
320        // discriminant(1)
321        inner.saturating_add(1)
322    }
323}
324
325/// Prepends the discriminant byte to an already-encoded inner value.
326fn encode_with_discriminant(disc: u8, inner: Cow<[u8]>) -> Cow<'static, [u8]> {
327    let mut buf = Vec::with_capacity(1 + inner.len());
328    buf.push(disc);
329    buf.extend_from_slice(&inner);
330    Cow::Owned(buf)
331}
332
333/// Decodes a [`CustomValue`](crate::dbms::custom_value::CustomValue) from the bytes after the discriminant.
334fn decode_custom_value(data: &[u8]) -> MemoryResult<Value> {
335    if data.len() < 2 {
336        return Err(MemoryError::DecodeError(DecodeError::TooShort));
337    }
338    let tag_len = u16::from_le_bytes([data[0], data[1]]) as usize;
339    if data.len() < 2 + tag_len + 2 {
340        return Err(MemoryError::DecodeError(DecodeError::TooShort));
341    }
342    let type_tag = String::from_utf8(data[2..2 + tag_len].to_vec())?;
343    let data_offset = 2 + tag_len;
344    let data_len = u16::from_le_bytes([data[data_offset], data[data_offset + 1]]) as usize;
345    if data.len() < data_offset + 2 + data_len {
346        return Err(MemoryError::DecodeError(DecodeError::TooShort));
347    }
348    let encoded = data[data_offset + 2..data_offset + 2 + data_len].to_vec();
349    Ok(Value::Custom(crate::dbms::custom_value::CustomValue {
350        type_tag,
351        encoded,
352        display: String::new(),
353    }))
354}
355
356/// Smallest encoded `Vec<Value>` entry: a `u32` size prefix followed by a
357/// discriminant-only value such as [`Value::Null`].
358const MIN_ENCODED_VEC_ENTRY_SIZE: usize = 4 + 1;
359
360/// Encodes a `Vec<Value>` as `[count: u32 LE] + [for each value: [size: u32 LE] + value.encode()]`.
361///
362/// This is used as the key type for B-tree indexes, supporting both single-column
363/// and composite indexes uniformly.
364impl Encode for Vec<Value> {
365    const SIZE: DataSize = DataSize::Dynamic;
366    const ALIGNMENT: PageOffset = DEFAULT_ALIGNMENT;
367
368    fn encode(&'_ self) -> Cow<'_, [u8]> {
369        let count = self.len() as u32;
370        let mut buf = Vec::new();
371        buf.extend_from_slice(&count.to_le_bytes());
372        for value in self {
373            let encoded = Encode::encode(value);
374            let size = encoded.len() as u32;
375            buf.extend_from_slice(&size.to_le_bytes());
376            buf.extend_from_slice(&encoded);
377        }
378        Cow::Owned(buf)
379    }
380
381    fn decode(data: Cow<[u8]>) -> MemoryResult<Self> {
382        if data.len() < 4 {
383            return Err(MemoryError::DecodeError(DecodeError::TooShort));
384        }
385        let count = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
386        let mut offset = 4;
387        // every entry needs at least its size prefix and a discriminant byte,
388        // so the remaining input bounds the count before anything is reserved
389        if count > (data.len() - offset) / MIN_ENCODED_VEC_ENTRY_SIZE {
390            return Err(MemoryError::DecodeError(DecodeError::TooShort));
391        }
392        let mut values = Vec::new();
393        // `DecodeError` has no allocation variant; a count the allocator cannot satisfy
394        // is reported like an input that is too short to hold that many entries
395        values
396            .try_reserve_exact(count)
397            .map_err(|_| MemoryError::DecodeError(DecodeError::TooShort))?;
398        for _ in 0..count {
399            if offset + 4 > data.len() {
400                return Err(MemoryError::DecodeError(DecodeError::TooShort));
401            }
402            let size = u32::from_le_bytes([
403                data[offset],
404                data[offset + 1],
405                data[offset + 2],
406                data[offset + 3],
407            ]) as usize;
408            offset += 4;
409            if offset + size > data.len() {
410                return Err(MemoryError::DecodeError(DecodeError::TooShort));
411            }
412            let value = Value::decode(Cow::Owned(data[offset..offset + size].to_vec()))?;
413            values.push(value);
414            offset += size;
415        }
416        Ok(values)
417    }
418
419    fn size(&self) -> MSize {
420        let mut total: MSize = 4; // count
421        for value in self {
422            // size prefix + encoded value
423            total = total.saturating_add(4).saturating_add(Encode::size(value));
424        }
425        total
426    }
427}
428
429#[cfg(test)]
430mod tests {
431
432    use uuid::Uuid;
433
434    use super::*;
435
436    #[test]
437    fn test_null() {
438        let int_value: Value = types::Int32(42).into();
439        assert!(!int_value.is_null());
440
441        let null_value = Value::Null;
442        assert!(null_value.is_null());
443    }
444
445    #[test]
446    fn test_value_conversion_blob() {
447        let blob = types::Blob(vec![1, 2, 3]);
448        let value: Value = blob.clone().into();
449        assert_eq!(value.as_blob(), Some(&blob));
450    }
451
452    #[test]
453    fn test_value_conversion_boolean() {
454        let boolean = types::Boolean(true);
455        let value: Value = boolean.into();
456        assert_eq!(value.as_boolean(), Some(&boolean));
457    }
458
459    #[test]
460    fn test_value_conversion_date() {
461        let date = types::Date {
462            year: 2023,
463            month: 3,
464            day: 15,
465        }; // Example date
466        let value: Value = date.into();
467        assert_eq!(value.as_date(), Some(&date));
468    }
469
470    #[test]
471    fn test_value_conversion_datetime() {
472        let datetime = types::DateTime {
473            year: 2023,
474            month: 3,
475            day: 15,
476            hour: 12,
477            minute: 30,
478            second: 45,
479            microsecond: 123456,
480            timezone_offset_minutes: 0,
481        }; // Example datetime
482        let value: Value = datetime.into();
483        assert_eq!(value.as_datetime(), Some(&datetime));
484    }
485
486    #[test]
487    fn test_value_conversion_decimal() {
488        let decimal = types::Decimal(rust_decimal::Decimal::new(12345, 2)); // 123.45
489        let value: Value = decimal.into();
490        assert_eq!(value.as_decimal(), Some(&decimal));
491    }
492
493    #[test]
494    fn test_value_conversion_int32() {
495        let int32 = types::Int32(1234567890);
496        let value: Value = int32.into();
497        assert_eq!(value.as_int32(), Some(&int32));
498    }
499
500    #[test]
501    fn test_value_conversion_int64() {
502        let int64 = types::Int64(1234567890);
503        let value: Value = int64.into();
504        assert_eq!(value.as_int64(), Some(&int64));
505    }
506
507    #[test]
508    fn test_value_conversion_text() {
509        let text = types::Text("Hello, World!".to_string());
510        let value: Value = text.clone().into();
511        assert_eq!(value.as_text(), Some(&text));
512    }
513
514    #[test]
515    fn test_value_conversion_uint32() {
516        let uint32 = types::Uint32(123456);
517        let value: Value = uint32.into();
518        assert_eq!(value.as_uint32(), Some(&uint32));
519    }
520
521    #[test]
522    fn test_value_conversion_uint64() {
523        let uint64 = types::Uint64(12345678901234);
524        let value: Value = uint64.into();
525        assert_eq!(value.as_uint64(), Some(&uint64));
526    }
527
528    #[test]
529    fn test_value_conversion_uuid() {
530        let uuid = types::Uuid(
531            Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").expect("failed to parse uuid"),
532        );
533        let value: Value = uuid.clone().into();
534        assert_eq!(value.as_uuid(), Some(&uuid));
535    }
536
537    #[test]
538    fn test_value_type_name() {
539        let int_value: Value = types::Int32(42).into();
540        assert_eq!(int_value.type_name(), "Int32");
541
542        let text_value: Value = types::Text("Hello".to_string()).into();
543        assert_eq!(text_value.type_name(), "Text");
544
545        let null_value = Value::Null;
546        assert_eq!(null_value.type_name(), "Null");
547    }
548
549    #[test]
550    fn test_value_from_str() {
551        let str_value = "Hello, DBMS!";
552
553        let value = Value::from_str(str_value).unwrap();
554        assert_eq!(value.as_text().unwrap().0, str_value);
555    }
556
557    #[test]
558    fn test_should_create_custom_value() {
559        let cv = crate::dbms::custom_value::CustomValue {
560            type_tag: "role".to_string(),
561            encoded: vec![0x01],
562            display: "Admin".to_string(),
563        };
564        let value = Value::Custom(cv.clone());
565        assert_eq!(value.as_custom(), Some(&cv));
566    }
567
568    #[test]
569    fn test_should_return_none_for_non_custom() {
570        let value = Value::Null;
571        assert_eq!(value.as_custom(), None);
572    }
573
574    #[test]
575    fn test_should_compare_custom_values() {
576        let a = Value::Custom(crate::dbms::custom_value::CustomValue {
577            type_tag: "role".to_string(),
578            encoded: vec![0x01],
579            display: "Admin".to_string(),
580        });
581        let b = Value::Custom(crate::dbms::custom_value::CustomValue {
582            type_tag: "role".to_string(),
583            encoded: vec![0x01],
584            display: "Admin".to_string(),
585        });
586        assert_eq!(a, b);
587    }
588
589    #[test]
590    fn test_should_order_custom_after_builtin() {
591        let builtin = Value::Uuid(types::Uuid::default());
592        let custom = Value::Custom(crate::dbms::custom_value::CustomValue {
593            type_tag: "role".to_string(),
594            encoded: vec![0x01],
595            display: "Admin".to_string(),
596        });
597        assert!(builtin < custom);
598    }
599
600    #[test]
601    fn test_should_get_custom_type_name() {
602        let cv = Value::Custom(crate::dbms::custom_value::CustomValue {
603            type_tag: "role".to_string(),
604            encoded: vec![0x01],
605            display: "Admin".to_string(),
606        });
607        assert_eq!(cv.type_name(), "Custom(role)");
608    }
609
610    // -- Encode round-trip tests for Value --
611
612    #[test]
613    fn test_encode_decode_null() {
614        let original = Value::Null;
615        let encoded = Encode::encode(&original);
616        assert_eq!(encoded.len(), 1);
617        let decoded = Value::decode(encoded).unwrap();
618        assert_eq!(original, decoded);
619    }
620
621    #[test]
622    fn test_encode_decode_uint32() {
623        let original = Value::Uint32(types::Uint32(42));
624        let encoded = Encode::encode(&original);
625        let decoded = Value::decode(encoded).unwrap();
626        assert_eq!(original, decoded);
627    }
628
629    #[test]
630    fn test_encode_decode_text() {
631        let original = Value::Text(types::Text("hello index".to_string()));
632        let encoded = Encode::encode(&original);
633        let decoded = Value::decode(encoded).unwrap();
634        assert_eq!(original, decoded);
635    }
636
637    #[test]
638    fn test_encode_decode_blob() {
639        let original = Value::Blob(types::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF]));
640        let encoded = Encode::encode(&original);
641        let decoded = Value::decode(encoded).unwrap();
642        assert_eq!(original, decoded);
643    }
644
645    #[test]
646    fn test_encode_decode_boolean() {
647        let original = Value::Boolean(types::Boolean(true));
648        let encoded = Encode::encode(&original);
649        let decoded = Value::decode(encoded).unwrap();
650        assert_eq!(original, decoded);
651    }
652
653    #[test]
654    fn test_encode_decode_date() {
655        let original = Value::Date(types::Date {
656            year: 2026,
657            month: 3,
658            day: 29,
659        });
660        let encoded = Encode::encode(&original);
661        let decoded = Value::decode(encoded).unwrap();
662        assert_eq!(original, decoded);
663    }
664
665    #[test]
666    fn test_encode_decode_datetime() {
667        let original = Value::DateTime(types::DateTime {
668            year: 2026,
669            month: 3,
670            day: 29,
671            hour: 14,
672            minute: 30,
673            second: 0,
674            microsecond: 0,
675            timezone_offset_minutes: 60,
676        });
677        let encoded = Encode::encode(&original);
678        let decoded = Value::decode(encoded).unwrap();
679        assert_eq!(original, decoded);
680    }
681
682    #[test]
683    fn test_encode_decode_decimal() {
684        let original = Value::Decimal(types::Decimal(rust_decimal::Decimal::new(12345, 2)));
685        let encoded = Encode::encode(&original);
686        let decoded = Value::decode(encoded).unwrap();
687        assert_eq!(original, decoded);
688    }
689
690    #[test]
691    fn test_encode_decode_int8() {
692        let original = Value::Int8(types::Int8(-42));
693        let encoded = Encode::encode(&original);
694        let decoded = Value::decode(encoded).unwrap();
695        assert_eq!(original, decoded);
696    }
697
698    #[test]
699    fn test_encode_decode_int16() {
700        let original = Value::Int16(types::Int16(-1000));
701        let encoded = Encode::encode(&original);
702        let decoded = Value::decode(encoded).unwrap();
703        assert_eq!(original, decoded);
704    }
705
706    #[test]
707    fn test_encode_decode_int32() {
708        let original = Value::Int32(types::Int32(-100_000));
709        let encoded = Encode::encode(&original);
710        let decoded = Value::decode(encoded).unwrap();
711        assert_eq!(original, decoded);
712    }
713
714    #[test]
715    fn test_encode_decode_int64() {
716        let original = Value::Int64(types::Int64(-9_000_000_000));
717        let encoded = Encode::encode(&original);
718        let decoded = Value::decode(encoded).unwrap();
719        assert_eq!(original, decoded);
720    }
721
722    #[test]
723    fn test_encode_decode_uint8() {
724        let original = Value::Uint8(types::Uint8(255));
725        let encoded = Encode::encode(&original);
726        let decoded = Value::decode(encoded).unwrap();
727        assert_eq!(original, decoded);
728    }
729
730    #[test]
731    fn test_encode_decode_uint16() {
732        let original = Value::Uint16(types::Uint16(60_000));
733        let encoded = Encode::encode(&original);
734        let decoded = Value::decode(encoded).unwrap();
735        assert_eq!(original, decoded);
736    }
737
738    #[test]
739    fn test_encode_decode_uint64() {
740        let original = Value::Uint64(types::Uint64(18_446_744_073_709_551_615));
741        let encoded = Encode::encode(&original);
742        let decoded = Value::decode(encoded).unwrap();
743        assert_eq!(original, decoded);
744    }
745
746    #[test]
747    fn test_encode_decode_uuid() {
748        let original = Value::Uuid(types::Uuid(
749            Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap(),
750        ));
751        let encoded = Encode::encode(&original);
752        let decoded = Value::decode(encoded).unwrap();
753        assert_eq!(original, decoded);
754    }
755
756    #[test]
757    fn test_encode_decode_custom() {
758        let original = Value::Custom(crate::dbms::custom_value::CustomValue {
759            type_tag: "role".to_string(),
760            encoded: vec![0x01, 0x02],
761            display: "Admin".to_string(),
762        });
763        let encoded = Encode::encode(&original);
764        let decoded = Value::decode(encoded).unwrap();
765        // Display is not preserved through encoding
766        assert_eq!(decoded.as_custom().unwrap().type_tag, "role");
767        assert_eq!(decoded.as_custom().unwrap().encoded, vec![0x01, 0x02]);
768    }
769
770    #[test]
771    fn test_encode_decode_invalid_discriminant() {
772        let data = Cow::Owned(vec![0xFF]);
773        let result = Value::decode(data);
774        assert!(result.is_err());
775    }
776
777    #[test]
778    fn test_encode_decode_empty_data() {
779        let data: Cow<[u8]> = Cow::Owned(vec![]);
780        let result = Value::decode(data);
781        assert!(result.is_err());
782    }
783
784    #[test]
785    fn test_value_size_matches_encoded_length() {
786        let values = vec![
787            Value::Null,
788            Value::Uint32(types::Uint32(42)),
789            Value::Text(types::Text("test".to_string())),
790            Value::Boolean(types::Boolean(false)),
791        ];
792        for value in &values {
793            let encoded = Encode::encode(value);
794            assert_eq!(
795                Encode::size(value) as usize,
796                encoded.len(),
797                "size mismatch for {value:?}"
798            );
799        }
800    }
801
802    // -- Encode round-trip tests for Vec<Value> --
803
804    #[test]
805    fn test_encode_decode_vec_single_value() {
806        let original = vec![Value::Uint32(types::Uint32(99))];
807        let encoded = Encode::encode(&original);
808        let decoded = Vec::<Value>::decode(encoded).unwrap();
809        assert_eq!(original, decoded);
810    }
811
812    #[test]
813    fn test_encode_decode_vec_composite() {
814        let original = vec![
815            Value::Text(types::Text("alice".to_string())),
816            Value::Uint32(types::Uint32(30)),
817        ];
818        let encoded = Encode::encode(&original);
819        let decoded = Vec::<Value>::decode(encoded).unwrap();
820        assert_eq!(original, decoded);
821    }
822
823    #[test]
824    fn test_encode_decode_vec_empty() {
825        let original: Vec<Value> = vec![];
826        let encoded = Encode::encode(&original);
827        let decoded = Vec::<Value>::decode(encoded).unwrap();
828        assert_eq!(original, decoded);
829    }
830
831    #[test]
832    fn test_vec_value_size_matches_encoded_length() {
833        let original = vec![
834            Value::Text(types::Text("hello".to_string())),
835            Value::Null,
836            Value::Int64(types::Int64(-1)),
837        ];
838        let encoded = Encode::encode(&original);
839        assert_eq!(Encode::size(&original) as usize, encoded.len());
840    }
841
842    #[test]
843    fn test_vec_value_decode_bounds_declared_count_by_input() {
844        // A header declaring one million values, followed by no value bytes.
845        let bytes = 1_000_000u32.to_le_bytes();
846
847        let (result, largest) = crate::test_alloc::largest_allocation_during(|| {
848            Vec::<Value>::decode(Cow::Borrowed(&bytes))
849        });
850
851        assert!(
852            matches!(result, Err(MemoryError::DecodeError(DecodeError::TooShort))),
853            "unexpected decode result: {result:?}"
854        );
855        // generous bound: a decoder that trusts the header would request megabytes
856        assert!(
857            largest < 1_024,
858            "decoder allocated {largest} bytes for a {}-byte input",
859            bytes.len()
860        );
861    }
862
863    #[test]
864    fn test_vec_value_decode_bounds_count_with_partial_input() {
865        // Declares u32::MAX values but carries only a single `Null` entry.
866        let mut bytes = u32::MAX.to_le_bytes().to_vec();
867        bytes.extend_from_slice(&1u32.to_le_bytes());
868        bytes.extend_from_slice(&Encode::encode(&Value::Null));
869
870        let (result, largest) = crate::test_alloc::largest_allocation_during(|| {
871            Vec::<Value>::decode(Cow::Borrowed(&bytes))
872        });
873
874        assert!(
875            matches!(result, Err(MemoryError::DecodeError(DecodeError::TooShort))),
876            "unexpected decode result: {result:?}"
877        );
878        // generous bound: a decoder that trusts the header would request megabytes
879        assert!(
880            largest < 1_024,
881            "decoder allocated {largest} bytes for a {}-byte input",
882            bytes.len()
883        );
884    }
885
886    // -- Encoded size limits --
887
888    fn custom_value(encoded_len: usize) -> Value {
889        Value::Custom(crate::dbms::custom_value::CustomValue {
890            type_tag: "t".to_string(),
891            encoded: vec![0xCD; encoded_len],
892            display: String::new(),
893        })
894    }
895
896    #[test]
897    fn test_value_round_trips_at_largest_encodable_length() {
898        // discriminant(1) + prefix(2) + 65_532 = MSize::MAX
899        let text = Value::Text(types::Text("x".repeat(65_532)));
900        // discriminant(1) + prefix(2) + 65_532 = MSize::MAX
901        let blob = Value::Blob(types::Blob(vec![0xAB; 65_532]));
902        // discriminant(1) + tag_len(2) + "t"(1) + data_len(2) + 65_529 = MSize::MAX
903        let custom = custom_value(65_529);
904
905        for value in [text, blob, custom] {
906            assert_eq!(Encode::size(&value), MSize::MAX);
907            let encoded = Encode::encode(&value);
908            assert_eq!(encoded.len(), MSize::MAX as usize);
909            let decoded = Value::decode(encoded).unwrap();
910            assert_eq!(decoded, value);
911        }
912    }
913
914    #[test]
915    fn test_value_size_saturates_when_payload_does_not_fit() {
916        let values = [
917            Value::Text(types::Text("x".repeat(65_533))),
918            Value::Text(types::Text("x".repeat(65_536))),
919            Value::Blob(types::Blob(vec![0xAB; 65_533])),
920            Value::Blob(types::Blob(vec![0xAB; 65_536])),
921            Value::Json(types::Json::from(serde_json::json!("x".repeat(65_534)))),
922            custom_value(65_530),
923            custom_value(65_536),
924            custom_value(131_072),
925        ];
926        for value in &values {
927            assert_eq!(
928                Encode::size(value),
929                MSize::MAX,
930                "size must saturate for {}",
931                value.type_name()
932            );
933        }
934    }
935
936    #[test]
937    fn test_vec_value_size_saturates_when_payload_does_not_fit() {
938        // each value fits on its own, the composite key does not
939        let original = vec![
940            Value::Text(types::Text("x".repeat(40_000))),
941            Value::Text(types::Text("y".repeat(40_000))),
942        ];
943        assert_eq!(Encode::size(&original), MSize::MAX);
944    }
945}