Skip to main content

reifydb_codec/encoded/
value.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_value::{
5	reifydb_assertions,
6	value::{Value, ordered_f32::OrderedF32, ordered_f64::OrderedF64, value_type::ValueType},
7};
8
9use super::shape::RowShape;
10use crate::encoded::row::EncodedRow;
11
12impl RowShape {
13	pub fn set_values(&self, row: &mut EncodedRow, values: &[Value]) {
14		reifydb_assertions! {
15			assert!(values.len() == self.fields().len());
16		}
17		for (idx, value) in values.iter().enumerate() {
18			self.set_value(row, idx, value)
19		}
20	}
21
22	pub fn set_value(&self, row: &mut EncodedRow, index: usize, val: &Value) {
23		let field = &self.fields()[index];
24		reifydb_assertions! {
25			assert!(
26				row.len() >= self.total_static_size(),
27				"row/shape size mismatch: row.len()={} < total_static_size()={}",
28				row.len(),
29				self.total_static_size()
30			);
31		}
32
33		let field_type = match field.constraint.get_type() {
34			ValueType::Option(inner) => *inner,
35			other => other,
36		};
37
38		match (field_type, val) {
39			(ValueType::Boolean, Value::Boolean(v)) => self.set_bool(row, index, *v),
40			(
41				ValueType::Boolean,
42				Value::None {
43					..
44				},
45			) => self.set_none(row, index),
46
47			(ValueType::Float4, Value::Float4(v)) => self.set_f32(row, index, v.value()),
48			(
49				ValueType::Float4,
50				Value::None {
51					..
52				},
53			) => self.set_none(row, index),
54
55			(ValueType::Float8, Value::Float8(v)) => self.set_f64(row, index, v.value()),
56			(
57				ValueType::Float8,
58				Value::None {
59					..
60				},
61			) => self.set_none(row, index),
62
63			(ValueType::Int1, Value::Int1(v)) => self.set_i8(row, index, *v),
64			(
65				ValueType::Int1,
66				Value::None {
67					..
68				},
69			) => self.set_none(row, index),
70
71			(ValueType::Int2, Value::Int2(v)) => self.set_i16(row, index, *v),
72			(
73				ValueType::Int2,
74				Value::None {
75					..
76				},
77			) => self.set_none(row, index),
78
79			(ValueType::Int4, Value::Int4(v)) => self.set_i32(row, index, *v),
80			(
81				ValueType::Int4,
82				Value::None {
83					..
84				},
85			) => self.set_none(row, index),
86
87			(ValueType::Int8, Value::Int8(v)) => self.set_i64(row, index, *v),
88			(
89				ValueType::Int8,
90				Value::None {
91					..
92				},
93			) => self.set_none(row, index),
94
95			(ValueType::Int16, Value::Int16(v)) => self.set_i128(row, index, *v),
96			(
97				ValueType::Int16,
98				Value::None {
99					..
100				},
101			) => self.set_none(row, index),
102
103			(ValueType::Utf8, Value::Utf8(v)) => self.set_utf8(row, index, v),
104			(
105				ValueType::Utf8,
106				Value::None {
107					..
108				},
109			) => self.set_none(row, index),
110
111			(ValueType::Uint1, Value::Uint1(v)) => self.set_u8(row, index, *v),
112			(
113				ValueType::Uint1,
114				Value::None {
115					..
116				},
117			) => self.set_none(row, index),
118
119			(ValueType::Uint2, Value::Uint2(v)) => self.set_u16(row, index, *v),
120			(
121				ValueType::Uint2,
122				Value::None {
123					..
124				},
125			) => self.set_none(row, index),
126
127			(ValueType::Uint4, Value::Uint4(v)) => self.set_u32(row, index, *v),
128			(
129				ValueType::Uint4,
130				Value::None {
131					..
132				},
133			) => self.set_none(row, index),
134
135			(ValueType::Uint8, Value::Uint8(v)) => self.set_u64(row, index, *v),
136			(
137				ValueType::Uint8,
138				Value::None {
139					..
140				},
141			) => self.set_none(row, index),
142
143			(ValueType::Uint16, Value::Uint16(v)) => self.set_u128(row, index, *v),
144			(
145				ValueType::Uint16,
146				Value::None {
147					..
148				},
149			) => self.set_none(row, index),
150
151			(ValueType::Date, Value::Date(v)) => self.set_date(row, index, *v),
152			(
153				ValueType::Date,
154				Value::None {
155					..
156				},
157			) => self.set_none(row, index),
158
159			(ValueType::DateTime, Value::DateTime(v)) => self.set_datetime(row, index, *v),
160			(
161				ValueType::DateTime,
162				Value::None {
163					..
164				},
165			) => self.set_none(row, index),
166
167			(ValueType::Time, Value::Time(v)) => self.set_time(row, index, *v),
168			(
169				ValueType::Time,
170				Value::None {
171					..
172				},
173			) => self.set_none(row, index),
174
175			(ValueType::Duration, Value::Duration(v)) => self.set_duration(row, index, *v),
176			(
177				ValueType::Duration,
178				Value::None {
179					..
180				},
181			) => self.set_none(row, index),
182
183			(ValueType::Uuid4, Value::Uuid4(v)) => self.set_uuid4(row, index, *v),
184			(
185				ValueType::Uuid4,
186				Value::None {
187					..
188				},
189			) => self.set_none(row, index),
190
191			(ValueType::Uuid7, Value::Uuid7(v)) => self.set_uuid7(row, index, *v),
192			(
193				ValueType::Uuid7,
194				Value::None {
195					..
196				},
197			) => self.set_none(row, index),
198
199			(ValueType::Blob, Value::Blob(v)) => self.set_blob(row, index, v),
200			(
201				ValueType::Blob,
202				Value::None {
203					..
204				},
205			) => self.set_none(row, index),
206
207			(ValueType::Int, Value::Int(v)) => self.set_int(row, index, v),
208			(ValueType::Uint, Value::Uint(v)) => self.set_uint(row, index, v),
209			(
210				ValueType::Int,
211				Value::None {
212					..
213				},
214			) => self.set_none(row, index),
215			(
216				ValueType::Uint,
217				Value::None {
218					..
219				},
220			) => self.set_none(row, index),
221
222			(ValueType::Decimal, Value::Decimal(v)) => self.set_decimal(row, index, v),
223			(
224				ValueType::Decimal,
225				Value::None {
226					..
227				},
228			) => self.set_none(row, index),
229			(ValueType::DictionaryId, Value::DictionaryId(id)) => self.set_dictionary_id(row, index, id),
230
231			(
232				ValueType::DictionaryId,
233				Value::None {
234					..
235				},
236			) => self.set_none(row, index),
237
238			(ValueType::IdentityId, Value::IdentityId(id)) => self.set_identity_id(row, index, *id),
239			(
240				ValueType::IdentityId,
241				Value::None {
242					..
243				},
244			) => self.set_none(row, index),
245
246			(
247				ValueType::Any,
248				Value::None {
249					..
250				},
251			) => self.set_none(row, index),
252			(ValueType::Any, Value::Any(inner)) => self.set_any(row, index, inner),
253			(ty, val) => unreachable!(
254				"set_value type mismatch at index {index}: column name={:?} declared_type={ty:?}, value={val:?}",
255				field.name,
256			),
257		}
258	}
259
260	pub fn get_value(&self, row: &EncodedRow, index: usize) -> Value {
261		let field = &self.fields()[index];
262		if !row.is_defined(index) {
263			return Value::none();
264		}
265		let field_type = match field.constraint.get_type() {
266			ValueType::Option(inner) => *inner,
267			other => other,
268		};
269
270		match field_type {
271			ValueType::Boolean => Value::Boolean(self.get_bool(row, index)),
272			ValueType::Float4 => OrderedF32::try_from(self.get_f32(row, index))
273				.map(Value::Float4)
274				.unwrap_or(Value::none()),
275			ValueType::Float8 => OrderedF64::try_from(self.get_f64(row, index))
276				.map(Value::Float8)
277				.unwrap_or(Value::none()),
278			ValueType::Int1 => Value::Int1(self.get_i8(row, index)),
279			ValueType::Int2 => Value::Int2(self.get_i16(row, index)),
280			ValueType::Int4 => Value::Int4(self.get_i32(row, index)),
281			ValueType::Int8 => Value::Int8(self.get_i64(row, index)),
282			ValueType::Int16 => Value::Int16(self.get_i128(row, index)),
283			ValueType::Utf8 => Value::Utf8(self.get_utf8(row, index).to_string()),
284			ValueType::Uint1 => Value::Uint1(self.get_u8(row, index)),
285			ValueType::Uint2 => Value::Uint2(self.get_u16(row, index)),
286			ValueType::Uint4 => Value::Uint4(self.get_u32(row, index)),
287			ValueType::Uint8 => Value::Uint8(self.get_u64(row, index)),
288			ValueType::Uint16 => Value::Uint16(self.get_u128(row, index)),
289			ValueType::Date => Value::Date(self.get_date(row, index)),
290			ValueType::DateTime => Value::DateTime(self.get_datetime(row, index)),
291			ValueType::Time => Value::Time(self.get_time(row, index)),
292			ValueType::Duration => Value::Duration(self.get_duration(row, index)),
293			ValueType::IdentityId => Value::IdentityId(self.get_identity_id(row, index)),
294			ValueType::Uuid4 => Value::Uuid4(self.get_uuid4(row, index)),
295			ValueType::Uuid7 => Value::Uuid7(self.get_uuid7(row, index)),
296			ValueType::Blob => Value::Blob(self.get_blob(row, index)),
297			ValueType::Int => Value::Int(self.get_int(row, index)),
298			ValueType::Uint => Value::Uint(self.get_uint(row, index)),
299			ValueType::Decimal => Value::Decimal(self.get_decimal(row, index)),
300			ValueType::DictionaryId => Value::DictionaryId(self.get_dictionary_id(row, index)),
301			ValueType::Option(_) => unreachable!("Option type already unwrapped"),
302			ValueType::Any => Value::Any(Box::new(self.get_any(row, index))),
303			ValueType::List(_) => unreachable!("List type cannot be stored in database"),
304			ValueType::Record(_) => unreachable!("Record type cannot be stored in database"),
305			ValueType::Tuple(_) => unreachable!("Tuple type cannot be stored in database"),
306		}
307	}
308}
309
310#[cfg(test)]
311#[allow(clippy::approx_constant)]
312pub mod tests {
313	use std::f64::consts::E;
314
315	use reifydb_runtime::context::{
316		clock::{Clock, MockClock},
317		rng::Rng,
318	};
319	use reifydb_value::value::{
320		Value,
321		blob::Blob,
322		constraint::TypeConstraint,
323		date::Date,
324		datetime::DateTime,
325		dictionary::{DictionaryEntryId, DictionaryId},
326		ordered_f32::OrderedF32,
327		ordered_f64::OrderedF64,
328		time::Time,
329		uuid::{Uuid4, Uuid7},
330		value_type::ValueType,
331	};
332
333	use crate::encoded::shape::{RowShape, RowShapeField};
334
335	fn test_clock_and_rng() -> (MockClock, Clock, Rng) {
336		let mock = MockClock::from_millis(1000);
337		let clock = Clock::Mock(mock.clone());
338		let rng = Rng::seeded(42);
339		(mock, clock, rng)
340	}
341
342	#[test]
343	fn test_set_utf8_with_dynamic_content() {
344		let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Int4, ValueType::Utf8]);
345		let mut row = shape.allocate();
346
347		let value1 = Value::Utf8("hello".to_string());
348		let value2 = Value::Int4(42);
349		let value3 = Value::Utf8("world".to_string());
350
351		shape.set_value(&mut row, 0, &value1);
352		shape.set_value(&mut row, 1, &value2);
353		shape.set_value(&mut row, 2, &value3);
354
355		assert_eq!(shape.get_utf8(&row, 0), "hello");
356		assert_eq!(shape.get_i32(&row, 1), 42);
357		assert_eq!(shape.get_utf8(&row, 2), "world");
358	}
359
360	#[test]
361	fn test_set_values_with_mixed_dynamic_content() {
362		let shape = RowShape::testing(&[
363			ValueType::Boolean,
364			ValueType::Utf8,
365			ValueType::Float4,
366			ValueType::Utf8,
367			ValueType::Int2,
368		]);
369		let mut row = shape.allocate();
370
371		let values = vec![
372			Value::Boolean(true),
373			Value::Utf8("first_string".to_string()),
374			Value::Float4(OrderedF32::try_from(3.14f32).unwrap()),
375			Value::Utf8("second_string".to_string()),
376			Value::Int2(-100),
377		];
378
379		shape.set_values(&mut row, &values);
380
381		assert_eq!(shape.get_bool(&row, 0), true);
382		assert_eq!(shape.get_utf8(&row, 1), "first_string");
383		assert_eq!(shape.get_f32(&row, 2), 3.14f32);
384		assert_eq!(shape.get_utf8(&row, 3), "second_string");
385		assert_eq!(shape.get_i16(&row, 4), -100);
386	}
387
388	#[test]
389	fn test_set_with_empty_and_large_utf8() {
390		let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Utf8, ValueType::Utf8]);
391		let mut row = shape.allocate();
392
393		let large_string = "X".repeat(2000);
394		let values = vec![
395			Value::Utf8("".to_string()),
396			Value::Utf8(large_string.clone()),
397			Value::Utf8("small".to_string()),
398		];
399
400		shape.set_values(&mut row, &values);
401
402		assert_eq!(shape.get_utf8(&row, 0), "");
403		assert_eq!(shape.get_utf8(&row, 1), large_string);
404		assert_eq!(shape.get_utf8(&row, 2), "small");
405		assert_eq!(shape.dynamic_section_size(&row), 2005); // 0 + 2000 + 5
406	}
407
408	#[test]
409	fn test_get_from_dynamic_content() {
410		let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Int8, ValueType::Utf8]);
411		let mut row = shape.allocate();
412
413		shape.set_utf8(&mut row, 0, "test_string");
414		shape.set_i64(&mut row, 1, 9876543210i64);
415		shape.set_utf8(&mut row, 2, "another_string");
416
417		let value0 = shape.get_value(&row, 0);
418		let value1 = shape.get_value(&row, 1);
419		let value2 = shape.get_value(&row, 2);
420
421		match value0 {
422			Value::Utf8(s) => assert_eq!(s, "test_string"),
423			_ => panic!("Expected UTF8 value"),
424		}
425
426		match value1 {
427			Value::Int8(i) => assert_eq!(i, 9876543210),
428			_ => panic!("Expected Int8 value"),
429		}
430
431		match value2 {
432			Value::Utf8(s) => assert_eq!(s, "another_string"),
433			_ => panic!("Expected UTF8 value"),
434		}
435	}
436
437	#[test]
438	fn test_set_none_with_utf8_fields() {
439		let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Boolean, ValueType::Utf8]);
440		let mut row = shape.allocate();
441
442		// Set some values
443		shape.set_value(&mut row, 0, &Value::Utf8("hello".to_string()));
444		shape.set_value(&mut row, 1, &Value::Boolean(true));
445		shape.set_value(&mut row, 2, &Value::Utf8("world".to_string()));
446
447		assert!(row.is_defined(0));
448		assert!(row.is_defined(1));
449		assert!(row.is_defined(2));
450
451		// Set some as undefined
452		shape.set_value(&mut row, 0, &Value::none());
453		shape.set_value(&mut row, 2, &Value::none());
454
455		assert!(!row.is_defined(0));
456		assert!(row.is_defined(1));
457		assert!(!row.is_defined(2));
458
459		assert_eq!(shape.get_bool(&row, 1), true);
460	}
461
462	#[test]
463	fn test_get_all_types_including_utf8() {
464		let shape = RowShape::testing(&[
465			ValueType::Boolean,
466			ValueType::Int1,
467			ValueType::Int2,
468			ValueType::Int4,
469			ValueType::Int8,
470			ValueType::Uint1,
471			ValueType::Uint2,
472			ValueType::Uint4,
473			ValueType::Uint8,
474			ValueType::Float4,
475			ValueType::Float8,
476			ValueType::Utf8,
477		]);
478		let mut row = shape.allocate();
479
480		shape.set_bool(&mut row, 0, true);
481		shape.set_i8(&mut row, 1, -42);
482		shape.set_i16(&mut row, 2, -1000i16);
483		shape.set_i32(&mut row, 3, -50000i32);
484		shape.set_i64(&mut row, 4, -3000000000i64);
485		shape.set_u8(&mut row, 5, 200u8);
486		shape.set_u16(&mut row, 6, 50000u16);
487		shape.set_u32(&mut row, 7, 3000000000u32);
488		shape.set_u64(&mut row, 8, 15000000000000000000u64);
489		shape.set_f32(&mut row, 9, 2.5);
490		shape.set_f64(&mut row, 10, 123.456789);
491		shape.set_utf8(&mut row, 11, "dynamic_string");
492
493		let values: Vec<Value> = (0..12).map(|i| shape.get_value(&row, i)).collect();
494
495		assert_eq!(values[0], Value::Boolean(true));
496		assert_eq!(values[1], Value::Int1(-42));
497		assert_eq!(values[2], Value::Int2(-1000));
498		assert_eq!(values[3], Value::Int4(-50000));
499		assert_eq!(values[4], Value::Int8(-3000000000));
500		assert_eq!(values[5], Value::Uint1(200));
501		assert_eq!(values[6], Value::Uint2(50000));
502		assert_eq!(values[7], Value::Uint4(3000000000));
503		assert_eq!(values[8], Value::Uint8(15000000000000000000));
504		assert_eq!(values[9], Value::Float4(OrderedF32::try_from(2.5f32).unwrap()));
505		assert_eq!(values[10], Value::Float8(OrderedF64::try_from(123.456789f64).unwrap()));
506		assert_eq!(values[11], Value::Utf8("dynamic_string".to_string()));
507	}
508
509	#[test]
510	fn test_set_values_sparse_with_utf8() {
511		let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Utf8, ValueType::Utf8, ValueType::Utf8]);
512		let mut row = shape.allocate();
513
514		// Only set some values
515		let values = vec![
516			Value::Utf8("first".to_string()),
517			Value::none(),
518			Value::Utf8("third".to_string()),
519			Value::none(),
520		];
521
522		shape.set_values(&mut row, &values);
523
524		assert!(row.is_defined(0));
525		assert!(!row.is_defined(1));
526		assert!(row.is_defined(2));
527		assert!(!row.is_defined(3));
528
529		assert_eq!(shape.get_utf8(&row, 0), "first");
530		assert_eq!(shape.get_utf8(&row, 2), "third");
531	}
532
533	#[test]
534	fn test_set_values_unicode_strings() {
535		let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Int4, ValueType::Utf8]);
536		let mut row = shape.allocate();
537
538		let values = vec![
539			Value::Utf8("πŸŽ‰πŸš€βœ¨".to_string()),
540			Value::Int4(123),
541			Value::Utf8("Hello δΈ–η•Œ".to_string()),
542		];
543
544		shape.set_values(&mut row, &values);
545
546		assert_eq!(shape.get_utf8(&row, 0), "πŸŽ‰πŸš€βœ¨");
547		assert_eq!(shape.get_i32(&row, 1), 123);
548		assert_eq!(shape.get_utf8(&row, 2), "Hello δΈ–η•Œ");
549	}
550
551	#[test]
552	fn test_static_fields_only_no_dynamic_with_values() {
553		let shape = RowShape::testing(&[ValueType::Boolean, ValueType::Int4, ValueType::Float8]);
554		let mut row = shape.allocate();
555
556		let values =
557			vec![Value::Boolean(false), Value::Int4(999), Value::Float8(OrderedF64::try_from(E).unwrap())];
558
559		shape.set_values(&mut row, &values);
560
561		// Verify no dynamic section
562		assert_eq!(shape.dynamic_section_size(&row), 0);
563		assert_eq!(row.len(), shape.total_static_size());
564
565		assert_eq!(shape.get_bool(&row, 0), false);
566		assert_eq!(shape.get_i32(&row, 1), 999);
567		assert_eq!(shape.get_f64(&row, 2), E);
568	}
569
570	#[test]
571	fn test_temporal_types_roundtrip() {
572		let shape = RowShape::testing(&[
573			ValueType::Date,
574			ValueType::DateTime,
575			ValueType::Time,
576			ValueType::Duration,
577		]);
578		let mut row = shape.allocate();
579
580		let original_values = vec![
581			Value::Date(Date::new(2025, 7, 15).unwrap()),
582			Value::DateTime(DateTime::from_ymd_hms(2025, 7, 15, 14, 30, 45).unwrap()),
583			Value::Time(Time::new(14, 30, 45, 123456789).unwrap()),
584			Value::duration_seconds(3600),
585		];
586
587		shape.set_values(&mut row, &original_values);
588
589		let retrieved_values: Vec<Value> = (0..4).map(|i| shape.get_value(&row, i)).collect();
590
591		assert_eq!(retrieved_values, original_values);
592	}
593
594	#[test]
595	fn test_temporal_types_with_undefined() {
596		let shape = RowShape::testing(&[
597			ValueType::Date,
598			ValueType::DateTime,
599			ValueType::Time,
600			ValueType::Duration,
601		]);
602		let mut row = shape.allocate();
603
604		let values = vec![
605			Value::Date(Date::new(2000, 1, 1).unwrap()),
606			Value::none(),
607			Value::Time(Time::default()),
608			Value::none(),
609		];
610
611		shape.set_values(&mut row, &values);
612
613		assert!(row.is_defined(0));
614		assert!(!row.is_defined(1));
615		assert!(row.is_defined(2));
616		assert!(!row.is_defined(3));
617
618		let retrieved_values: Vec<Value> = (0..4).map(|i| shape.get_value(&row, i)).collect();
619
620		assert_eq!(retrieved_values[0], values[0]);
621		assert_eq!(retrieved_values[1], Value::none());
622		assert_eq!(retrieved_values[2], values[2]);
623		assert_eq!(retrieved_values[3], Value::none());
624	}
625
626	#[test]
627	fn test_mixed_temporal_and_regular_types() {
628		let shape = RowShape::testing(&[
629			ValueType::Boolean,
630			ValueType::Date,
631			ValueType::Utf8,
632			ValueType::DateTime,
633			ValueType::Int4,
634			ValueType::Time,
635			ValueType::Duration,
636		]);
637		let mut row = shape.allocate();
638
639		let values = vec![
640			Value::Boolean(true),
641			Value::Date(Date::new(1985, 10, 26).unwrap()),
642			Value::Utf8("time travel".to_string()),
643			Value::DateTime(DateTime::new(2015, 10, 21, 16, 29, 0, 0).unwrap()),
644			Value::Int4(88),
645			Value::Time(Time::new(12, 0, 0, 0).unwrap()),
646			Value::duration_minutes(30),
647		];
648
649		shape.set_values(&mut row, &values);
650
651		let retrieved_values: Vec<Value> = (0..7).map(|i| shape.get_value(&row, i)).collect();
652
653		assert_eq!(retrieved_values, values);
654	}
655
656	#[test]
657	fn test_roundtrip_with_dynamic_content() {
658		let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Int2, ValueType::Utf8, ValueType::Float4]);
659		let mut row = shape.allocate();
660
661		let original_values = vec![
662			Value::Utf8("roundtrip_test".to_string()),
663			Value::Int2(32000),
664			Value::Utf8("".to_string()),
665			Value::Float4(OrderedF32::try_from(1.5f32).unwrap()),
666		];
667
668		// Set values
669		shape.set_values(&mut row, &original_values);
670
671		// Get values back
672		let retrieved_values: Vec<Value> = (0..4).map(|i| shape.get_value(&row, i)).collect();
673
674		assert_eq!(retrieved_values, original_values);
675	}
676
677	#[test]
678	fn test_blob_roundtrip() {
679		let shape = RowShape::testing(&[ValueType::Blob, ValueType::Int4, ValueType::Blob]);
680		let mut row = shape.allocate();
681
682		let blob1 = Blob::new(vec![0xDE, 0xAD, 0xBE, 0xEF]);
683		let blob2 = Blob::new(vec![]);
684		let values = vec![Value::Blob(blob1.clone()), Value::Int4(42), Value::Blob(blob2.clone())];
685
686		shape.set_values(&mut row, &values);
687
688		let retrieved_values: Vec<Value> = (0..3).map(|i| shape.get_value(&row, i)).collect();
689
690		assert_eq!(retrieved_values, values);
691
692		// Verify blob content directly
693		match &retrieved_values[0] {
694			Value::Blob(b) => assert_eq!(b.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]),
695			_ => panic!("Expected Blob value"),
696		}
697
698		match &retrieved_values[2] {
699			Value::Blob(b) => assert!(b.is_empty()),
700			_ => panic!("Expected Blob value"),
701		}
702	}
703
704	#[test]
705	fn test_blob_with_undefined() {
706		let shape = RowShape::testing(&[ValueType::Blob, ValueType::Blob, ValueType::Blob]);
707		let mut row = shape.allocate();
708
709		let values = vec![
710			Value::Blob(Blob::new(vec![0x00, 0x01, 0x02])),
711			Value::none(),
712			Value::Blob(Blob::new(vec![0xFF, 0xFE])),
713		];
714
715		shape.set_values(&mut row, &values);
716
717		assert!(row.is_defined(0));
718		assert!(!row.is_defined(1));
719		assert!(row.is_defined(2));
720
721		let retrieved_values: Vec<Value> = (0..3).map(|i| shape.get_value(&row, i)).collect();
722
723		assert_eq!(retrieved_values[0], values[0]);
724		assert_eq!(retrieved_values[1], Value::none());
725		assert_eq!(retrieved_values[2], values[2]);
726	}
727
728	#[test]
729	fn test_uuid_roundtrip() {
730		let (_, clock, rng) = test_clock_and_rng();
731		let shape = RowShape::testing(&[ValueType::Uuid4, ValueType::Uuid7, ValueType::Int4]);
732		let mut row = shape.allocate();
733
734		let uuid4 = Uuid4::generate();
735		let uuid7 = Uuid7::generate(&clock, &rng);
736		let values = vec![Value::Uuid4(uuid4), Value::Uuid7(uuid7), Value::Int4(123)];
737
738		shape.set_values(&mut row, &values);
739
740		let retrieved_values: Vec<Value> = (0..3).map(|i| shape.get_value(&row, i)).collect();
741
742		assert_eq!(retrieved_values, values);
743	}
744
745	#[test]
746	fn test_uuid_with_undefined() {
747		let (_, clock, rng) = test_clock_and_rng();
748		let shape = RowShape::testing(&[ValueType::Uuid4, ValueType::Uuid7]);
749		let mut row = shape.allocate();
750
751		let values = vec![Value::none(), Value::Uuid7(Uuid7::generate(&clock, &rng))];
752
753		shape.set_values(&mut row, &values);
754
755		assert!(!row.is_defined(0));
756		assert!(row.is_defined(1));
757
758		let retrieved_values: Vec<Value> = (0..2).map(|i| shape.get_value(&row, i)).collect();
759
760		assert_eq!(retrieved_values[0], Value::none());
761		assert_eq!(retrieved_values[1], values[1]);
762	}
763
764	#[test]
765	fn test_mixed_blob_row_number_uuid_types() {
766		let (_, clock, rng) = test_clock_and_rng();
767		let shape = RowShape::testing(&[
768			ValueType::Blob,
769			ValueType::Int16,
770			ValueType::Uuid4,
771			ValueType::Utf8,
772			ValueType::Uuid7,
773			ValueType::Int4,
774		]);
775		let mut row = shape.allocate();
776
777		let values = vec![
778			Value::Blob(Blob::new(vec![0xCA, 0xFE, 0xBA, 0xBE])),
779			Value::Int16(42424242i128),
780			Value::Uuid4(Uuid4::generate()),
781			Value::Utf8("mixed types test".to_string()),
782			Value::Uuid7(Uuid7::generate(&clock, &rng)),
783			Value::Int4(-999),
784		];
785
786		shape.set_values(&mut row, &values);
787
788		let retrieved_values: Vec<Value> = (0..6).map(|i| shape.get_value(&row, i)).collect();
789
790		assert_eq!(retrieved_values, values);
791
792		// Verify dynamic content exists (for blob and utf8)
793		assert!(shape.dynamic_section_size(&row) > 0);
794	}
795
796	#[test]
797	fn test_all_types_comprehensive() {
798		// except encoded id
799		let (_, clock, rng) = test_clock_and_rng();
800
801		let shape = RowShape::testing(&[
802			ValueType::Boolean,
803			ValueType::Int1,
804			ValueType::Int2,
805			ValueType::Int4,
806			ValueType::Int8,
807			ValueType::Int16,
808			ValueType::Uint1,
809			ValueType::Uint2,
810			ValueType::Uint4,
811			ValueType::Uint8,
812			ValueType::Uint16,
813			ValueType::Float4,
814			ValueType::Float8,
815			ValueType::Utf8,
816			ValueType::Date,
817			ValueType::DateTime,
818			ValueType::Time,
819			ValueType::Duration,
820			ValueType::Uuid4,
821			ValueType::Uuid7,
822			ValueType::Blob,
823		]);
824		let mut row = shape.allocate();
825
826		let values = vec![
827			Value::Boolean(true),
828			Value::Int1(-128),
829			Value::Int2(-32768),
830			Value::Int4(-2147483648),
831			Value::Int8(-9223372036854775808),
832			Value::Int16(-170141183460469231731687303715884105728),
833			Value::Uint1(255),
834			Value::Uint2(65535),
835			Value::Uint4(4294967295),
836			Value::Uint8(18446744073709551615),
837			Value::Uint16(340282366920938463463374607431768211455),
838			Value::Float4(OrderedF32::try_from(3.14159f32).unwrap()),
839			Value::Float8(OrderedF64::try_from(2.718281828459045).unwrap()),
840			Value::Utf8("comprehensive test".to_string()),
841			Value::Date(Date::new(2025, 12, 31).unwrap()),
842			Value::DateTime(DateTime::new(2025, 1, 1, 0, 0, 0, 0).unwrap()),
843			Value::Time(Time::new(23, 59, 59, 999999999).unwrap()),
844			Value::duration_hours(24),
845			Value::Uuid4(Uuid4::generate()),
846			Value::Uuid7(Uuid7::generate(&clock, &rng)),
847			Value::Blob(Blob::new(vec![
848				0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
849				0xEE, 0xFF,
850			])),
851		];
852
853		shape.set_values(&mut row, &values);
854
855		let retrieved_values: Vec<Value> = (0..21).map(|i| shape.get_value(&row, i)).collect();
856
857		assert_eq!(retrieved_values, values);
858
859		// Verify all fields are defined
860		for i in 0..21 {
861			assert!(row.is_defined(i), "Field {} should be defined", i);
862		}
863	}
864
865	#[test]
866	fn test_dictionary_id_roundtrip_u4() {
867		let constraint = TypeConstraint::dictionary(DictionaryId::from(42u64), ValueType::Uint4);
868		let shape = RowShape::new(vec![RowShapeField::new("status", constraint)]);
869
870		let mut row = shape.allocate();
871		let entry = DictionaryEntryId::U4(7);
872		shape.set_value(&mut row, 0, &Value::DictionaryId(entry));
873
874		assert!(row.is_defined(0));
875		let retrieved = shape.get_value(&row, 0);
876		assert_eq!(retrieved, Value::DictionaryId(DictionaryEntryId::U4(7)));
877	}
878
879	#[test]
880	fn test_dictionary_id_roundtrip_u2() {
881		let constraint = TypeConstraint::dictionary(DictionaryId::from(10u64), ValueType::Uint2);
882		let shape = RowShape::new(vec![RowShapeField::new("category", constraint)]);
883
884		let mut row = shape.allocate();
885		let entry = DictionaryEntryId::U2(500);
886		shape.set_value(&mut row, 0, &Value::DictionaryId(entry));
887
888		assert!(row.is_defined(0));
889		let retrieved = shape.get_value(&row, 0);
890		assert_eq!(retrieved, Value::DictionaryId(DictionaryEntryId::U2(500)));
891	}
892
893	#[test]
894	fn test_dictionary_id_roundtrip_u8() {
895		let constraint = TypeConstraint::dictionary(DictionaryId::from(99u64), ValueType::Uint8);
896		let shape = RowShape::new(vec![RowShapeField::new("tag", constraint)]);
897
898		let mut row = shape.allocate();
899		let entry = DictionaryEntryId::U8(123456789);
900		shape.set_value(&mut row, 0, &Value::DictionaryId(entry));
901
902		assert!(row.is_defined(0));
903		let retrieved = shape.get_value(&row, 0);
904		assert_eq!(retrieved, Value::DictionaryId(DictionaryEntryId::U8(123456789)));
905	}
906
907	#[test]
908	fn test_dictionary_id_with_undefined() {
909		let constraint = TypeConstraint::dictionary(DictionaryId::from(1u64), ValueType::Uint4);
910		let shape = RowShape::new(vec![
911			RowShapeField::new("dict_col", constraint),
912			RowShapeField::unconstrained("int_col", ValueType::Int4),
913		]);
914
915		let mut row = shape.allocate();
916		shape.set_value(&mut row, 0, &Value::none());
917		shape.set_value(&mut row, 1, &Value::Int4(42));
918
919		assert!(!row.is_defined(0));
920		assert!(row.is_defined(1));
921
922		assert_eq!(shape.get_value(&row, 0), Value::none());
923		assert_eq!(shape.get_value(&row, 1), Value::Int4(42));
924	}
925
926	#[test]
927	fn test_dictionary_id_mixed_with_other_types() {
928		let dict_constraint = TypeConstraint::dictionary(DictionaryId::from(5u64), ValueType::Uint4);
929		let shape = RowShape::new(vec![
930			RowShapeField::unconstrained("id", ValueType::Int4),
931			RowShapeField::new("status", dict_constraint),
932			RowShapeField::unconstrained("name", ValueType::Utf8),
933		]);
934
935		let mut row = shape.allocate();
936		let values = vec![
937			Value::Int4(100),
938			Value::DictionaryId(DictionaryEntryId::U4(3)),
939			Value::Utf8("test".to_string()),
940		];
941		shape.set_values(&mut row, &values);
942
943		let retrieved: Vec<Value> = (0..3).map(|i| shape.get_value(&row, i)).collect();
944		assert_eq!(retrieved, values);
945	}
946
947	// Mirrors CatalogStore::set_config (crates/catalog/src/store/config/set.rs), which always
948	// wraps a config's value in Value::any(..) before storing it, even when that value is itself
949	// None (e.g. METRICS_PROFILER_SNAPSHOT_INTERVAL's default, Value::None { inner: Duration },
950	// meaning "snapshotting disabled"). `Value::PartialEq` treats all `Value::None` as equal
951	// regardless of `inner` (crates/value/src/value/mod.rs), so these destructure and compare
952	// `inner` directly rather than relying on `assert_eq!` against the wrapped value.
953
954	#[test]
955	fn test_any_field_stores_wrapped_none_duration() {
956		let shape = RowShape::testing(&[ValueType::Any]);
957		let mut row = shape.allocate();
958
959		shape.set_value(&mut row, 0, &Value::any(Value::none_of(ValueType::Duration)));
960
961		assert!(row.is_defined(0), "the field holds a stored Any value, not an absent one");
962		match shape.get_value(&row, 0) {
963			Value::Any(inner) => match *inner {
964				Value::None {
965					inner,
966				} => assert_eq!(inner, ValueType::Duration),
967				other => panic!("expected Value::None inside Any, got {other:?}"),
968			},
969			other => panic!("expected Value::Any, got {other:?}"),
970		}
971	}
972
973	#[test]
974	fn test_any_field_stores_wrapped_none_nested_option_duration() {
975		let shape = RowShape::testing(&[ValueType::Any]);
976		let mut row = shape.allocate();
977
978		let inner_ty = ValueType::Option(Box::new(ValueType::Duration));
979		shape.set_value(&mut row, 0, &Value::any(Value::none_of(inner_ty.clone())));
980
981		match shape.get_value(&row, 0) {
982			Value::Any(inner) => match *inner {
983				Value::None {
984					inner,
985				} => assert_eq!(inner, inner_ty),
986				other => panic!("expected Value::None inside Any, got {other:?}"),
987			},
988			other => panic!("expected Value::Any, got {other:?}"),
989		}
990	}
991}