Skip to main content

reifydb_codec/encoded/
any.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4#[cfg(reifydb_assertions)]
5use reifydb_value::value::value_type::ValueType;
6use reifydb_value::{reifydb_assertions, value::Value};
7
8use crate::{
9	encoded::{row::EncodedRow, shape::RowShape},
10	value::{decode_value, encode_value},
11};
12
13impl RowShape {
14	pub fn set_any(&self, row: &mut EncodedRow, index: usize, value: &Value) {
15		reifydb_assertions! {
16			assert!(
17				row.len() >= self.total_static_size(),
18				"row/shape size mismatch: row.len()={} < total_static_size()={}",
19				row.len(),
20				self.total_static_size()
21			);
22			assert_eq!(*self.fields()[index].constraint.get_type().inner_type(), ValueType::Any);
23		}
24		let encoded = encode_value(value).expect("unsupported value in any row field");
25		self.replace_dynamic_data(row, index, &encoded);
26	}
27
28	pub fn get_any(&self, row: &EncodedRow, index: usize) -> Value {
29		let field = &self.fields()[index];
30		reifydb_assertions! {
31			assert!(
32				row.len() >= self.total_static_size(),
33				"row/shape size mismatch: row.len()={} < total_static_size()={}",
34				row.len(),
35				self.total_static_size()
36			);
37			assert_eq!(*field.constraint.get_type().inner_type(), ValueType::Any);
38		}
39
40		let ref_slice = &row.as_slice()[field.offset as usize..field.offset as usize + 8];
41		let offset = u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]]) as usize;
42		let length = u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]]) as usize;
43
44		let dynamic_start = self.dynamic_section_start();
45		let data_start = dynamic_start + offset;
46		let data_slice = &row.as_slice()[data_start..data_start + length];
47
48		decode_value(data_slice).expect("corrupt any row field bytes")
49	}
50}
51
52#[cfg(test)]
53pub mod tests {
54	use crate::value::{decode_value as decode_value_codec, encode_value as encode_value_codec};
55
56	pub fn encode_value(value: &Value) -> Vec<u8> {
57		encode_value_codec(value).unwrap()
58	}
59
60	pub fn decode_value(bytes: &[u8]) -> Value {
61		decode_value_codec(bytes).unwrap()
62	}
63
64	use std::f64::consts::E;
65
66	use reifydb_runtime::context::{
67		clock::{Clock, MockClock},
68		rng::Rng,
69	};
70	use reifydb_value::value::{
71		Value,
72		blob::Blob,
73		date::Date,
74		datetime::DateTime,
75		ordered_f32::OrderedF32,
76		ordered_f64::OrderedF64,
77		time::Time,
78		uuid::{Uuid4, Uuid7},
79		value_type::ValueType,
80	};
81
82	use crate::encoded::shape::RowShape;
83
84	fn test_clock_and_rng() -> (MockClock, Clock, Rng) {
85		let mock = MockClock::from_millis(1000);
86		let clock = Clock::Mock(mock.clone());
87		let rng = Rng::seeded(42);
88		(mock, clock, rng)
89	}
90
91	#[test]
92	fn test_any_boolean() {
93		let shape = RowShape::testing(&[ValueType::Any]);
94		let mut row = shape.allocate();
95		shape.set_any(&mut row, 0, &Value::Boolean(true));
96		assert_eq!(shape.get_any(&row, 0), Value::Boolean(true));
97	}
98
99	#[test]
100	fn test_any_integers() {
101		let shape = RowShape::testing(&[ValueType::Any]);
102
103		let cases: &[Value] = &[
104			Value::Int1(-42),
105			Value::Int2(-1000),
106			Value::Int4(-100000),
107			Value::Int8(i64::MIN),
108			Value::Int16(i128::MAX),
109			Value::Uint1(255),
110			Value::Uint2(65535),
111			Value::Uint4(u32::MAX),
112			Value::Uint8(u64::MAX),
113			Value::Uint16(u128::MAX),
114		];
115
116		for case in cases {
117			let mut row = shape.allocate();
118			shape.set_any(&mut row, 0, case);
119			assert_eq!(&shape.get_any(&row, 0), case);
120		}
121	}
122
123	#[test]
124	fn test_any_floats() {
125		let shape = RowShape::testing(&[ValueType::Any]);
126
127		let f4 = Value::Float4(OrderedF32::try_from(3.14f32).unwrap());
128		let mut row = shape.allocate();
129		shape.set_any(&mut row, 0, &f4);
130		assert_eq!(shape.get_any(&row, 0), f4);
131
132		let f8 = Value::Float8(OrderedF64::try_from(E).unwrap());
133		let mut row2 = shape.allocate();
134		shape.set_any(&mut row2, 0, &f8);
135		assert_eq!(shape.get_any(&row2, 0), f8);
136	}
137
138	#[test]
139	fn test_any_temporal() {
140		let shape = RowShape::testing(&[ValueType::Any]);
141
142		let date = Value::Date(Date::new(2025, 7, 4).unwrap());
143		let mut row = shape.allocate();
144		shape.set_any(&mut row, 0, &date);
145		assert_eq!(shape.get_any(&row, 0), date);
146
147		let dt = Value::DateTime(DateTime::new(2025, 1, 1, 12, 0, 0, 0).unwrap());
148		let mut row2 = shape.allocate();
149		shape.set_any(&mut row2, 0, &dt);
150		assert_eq!(shape.get_any(&row2, 0), dt);
151
152		let t = Value::Time(Time::new(14, 30, 45, 123456789).unwrap());
153		let mut row3 = shape.allocate();
154		shape.set_any(&mut row3, 0, &t);
155		assert_eq!(shape.get_any(&row3, 0), t);
156
157		let dur = Value::duration_seconds(3600);
158		let mut row4 = shape.allocate();
159		shape.set_any(&mut row4, 0, &dur);
160		assert_eq!(shape.get_any(&row4, 0), dur);
161	}
162
163	#[test]
164	fn test_any_uuid() {
165		let (_, clock, rng) = test_clock_and_rng();
166		let shape = RowShape::testing(&[ValueType::Any]);
167
168		let u4 = Value::Uuid4(Uuid4::generate());
169		let mut row = shape.allocate();
170		shape.set_any(&mut row, 0, &u4);
171		assert_eq!(shape.get_any(&row, 0), u4);
172
173		let u7 = Value::Uuid7(Uuid7::generate(&clock, &rng));
174		let mut row2 = shape.allocate();
175		shape.set_any(&mut row2, 0, &u7);
176		assert_eq!(shape.get_any(&row2, 0), u7);
177	}
178
179	#[test]
180	fn test_any_utf8() {
181		let shape = RowShape::testing(&[ValueType::Any]);
182		let v = Value::Utf8("hello, world!".to_string());
183		let mut row = shape.allocate();
184		shape.set_any(&mut row, 0, &v);
185		assert_eq!(shape.get_any(&row, 0), v);
186	}
187
188	#[test]
189	fn test_any_blob() {
190		let shape = RowShape::testing(&[ValueType::Any]);
191		let v = Value::Blob(Blob::from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]));
192		let mut row = shape.allocate();
193		shape.set_any(&mut row, 0, &v);
194		assert_eq!(shape.get_any(&row, 0), v);
195	}
196
197	#[test]
198	fn test_any_none_via_set_value() {
199		let shape = RowShape::testing(&[ValueType::Any]);
200		let mut row = shape.allocate();
201		shape.set_value(&mut row, 0, &Value::none());
202		assert!(!row.is_defined(0));
203		assert_eq!(shape.get_value(&row, 0), Value::none());
204	}
205
206	#[test]
207	fn test_any_roundtrip_via_set_get_value() {
208		let shape = RowShape::testing(&[ValueType::Any]);
209
210		let cases: &[Value] = &[
211			Value::Boolean(false),
212			Value::Int4(42),
213			Value::Utf8("test".to_string()),
214			Value::Uint8(1234567890),
215		];
216
217		for inner in cases {
218			let wrapped = Value::any(inner.clone());
219			let mut row = shape.allocate();
220			shape.set_value(&mut row, 0, &wrapped);
221			let retrieved = shape.get_value(&row, 0);
222			assert_eq!(retrieved, wrapped, "roundtrip failed for {:?}", inner);
223		}
224	}
225
226	#[test]
227	fn test_any_multiple_fields() {
228		let shape = RowShape::testing(&[ValueType::Any, ValueType::Int4, ValueType::Any]);
229		let mut row = shape.allocate();
230
231		shape.set_any(&mut row, 0, &Value::Utf8("first".to_string()));
232		shape.set_i32(&mut row, 1, 99);
233		shape.set_any(&mut row, 2, &Value::Boolean(true));
234
235		assert_eq!(shape.get_any(&row, 0), Value::Utf8("first".to_string()));
236		assert_eq!(shape.get_i32(&row, 1), 99);
237		assert_eq!(shape.get_any(&row, 2), Value::Boolean(true));
238	}
239
240	#[test]
241	fn test_update_any() {
242		let shape = RowShape::testing(&[ValueType::Any]);
243		let mut row = shape.allocate();
244
245		shape.set_any(&mut row, 0, &Value::Int4(42));
246		assert_eq!(shape.get_any(&row, 0), Value::Int4(42));
247
248		// Overwrite with a different type
249		shape.set_any(&mut row, 0, &Value::Utf8("hello".to_string()));
250		assert_eq!(shape.get_any(&row, 0), Value::Utf8("hello".to_string()));
251
252		// Overwrite again with boolean
253		shape.set_any(&mut row, 0, &Value::Boolean(true));
254		assert_eq!(shape.get_any(&row, 0), Value::Boolean(true));
255	}
256
257	#[test]
258	fn test_update_any_with_other_dynamic_fields() {
259		let shape = RowShape::testing(&[ValueType::Any, ValueType::Utf8, ValueType::Any]);
260		let mut row = shape.allocate();
261
262		shape.set_any(&mut row, 0, &Value::Int4(1));
263		shape.set_utf8(&mut row, 1, "middle");
264		shape.set_any(&mut row, 2, &Value::Boolean(false));
265
266		// Update first any with a larger value
267		shape.set_any(&mut row, 0, &Value::Utf8("a long string value".to_string()));
268
269		assert_eq!(shape.get_any(&row, 0), Value::Utf8("a long string value".to_string()));
270		assert_eq!(shape.get_utf8(&row, 1), "middle");
271		assert_eq!(shape.get_any(&row, 2), Value::Boolean(false));
272	}
273
274	// `Value::PartialEq` treats every `Value::None { .. }` as equal regardless of `inner`
275	// (see crates/value/src/value/mod.rs), so these tests destructure and compare `inner`
276	// directly instead of using `assert_eq!(decoded, Value::none_of(ty))`, which would pass
277	// vacuously even if the inner type were lost or wrong.
278
279	#[test]
280	fn test_encode_decode_none_various_inner_types() {
281		// Config defaults such as METRICS_PROFILER_SNAPSHOT_INTERVAL use
282		// Value::None { inner: Duration } to mean "disabled". The Any encoding must round-trip
283		// that sentinel for any inner type, not just concrete values.
284		let cases: &[ValueType] = &[
285			ValueType::Boolean,
286			ValueType::Int4,
287			ValueType::Uint8,
288			ValueType::Utf8,
289			ValueType::Blob,
290			ValueType::Date,
291			ValueType::DateTime,
292			ValueType::Time,
293			ValueType::Duration,
294			ValueType::Uuid4,
295		];
296
297		for ty in cases {
298			let encoded = encode_value(&Value::none_of(ty.clone()));
299			match decode_value(&encoded) {
300				Value::None {
301					inner,
302				} => assert_eq!(&inner, ty, "inner type lost for {ty}"),
303				other => panic!("expected Value::None for {ty}, got {other:?}"),
304			}
305		}
306	}
307
308	#[test]
309	fn test_encode_decode_none_nested_option_duration() {
310		// Option<Option<Duration>>::None must round-trip distinctly from Option<Duration>::None:
311		// the inner type carries the full nesting, not just the base scalar.
312		let inner_ty = ValueType::Option(Box::new(ValueType::Duration));
313
314		let encoded = encode_value(&Value::none_of(inner_ty.clone()));
315		match decode_value(&encoded) {
316			Value::None {
317				inner,
318			} => assert_eq!(inner, inner_ty),
319			other => panic!("expected Value::None, got {other:?}"),
320		}
321	}
322
323	#[test]
324	fn test_encode_decode_none_triple_nested_option() {
325		// Nesting depth must generalize past 2 levels, not just the one depth that happens to
326		// survive the current "high bit means Option" type-tag scheme.
327		let inner_ty = ValueType::Option(Box::new(ValueType::Option(Box::new(ValueType::Option(Box::new(
328			ValueType::Duration,
329		))))));
330
331		let encoded = encode_value(&Value::none_of(inner_ty.clone()));
332		match decode_value(&encoded) {
333			Value::None {
334				inner,
335			} => assert_eq!(inner, inner_ty),
336			other => panic!("expected Value::None, got {other:?}"),
337		}
338	}
339
340	#[test]
341	fn test_any_set_get_none_duration() {
342		let shape = RowShape::testing(&[ValueType::Any]);
343		let mut row = shape.allocate();
344
345		shape.set_any(&mut row, 0, &Value::none_of(ValueType::Duration));
346
347		assert!(
348			row.is_defined(0),
349			"an Any field holding a None-of-Duration is a stored value, not an unset field"
350		);
351		match shape.get_any(&row, 0) {
352			Value::None {
353				inner,
354			} => assert_eq!(inner, ValueType::Duration),
355			other => panic!("expected Value::None, got {other:?}"),
356		}
357	}
358
359	#[test]
360	fn test_any_set_get_none_nested_option_duration() {
361		let shape = RowShape::testing(&[ValueType::Any]);
362		let mut row = shape.allocate();
363
364		let inner_ty = ValueType::Option(Box::new(ValueType::Duration));
365		shape.set_any(&mut row, 0, &Value::none_of(inner_ty.clone()));
366
367		match shape.get_any(&row, 0) {
368			Value::None {
369				inner,
370			} => assert_eq!(inner, inner_ty),
371			other => panic!("expected Value::None, got {other:?}"),
372		}
373	}
374}