Skip to main content

reifydb_core/
row.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_codec::row::{
5	bytes::EncodedBytes,
6	shape::{RowFamily, RowShape, RowShapeField},
7};
8use reifydb_value::value::{
9	constraint::{Constraint, TypeConstraint},
10	duration::Duration,
11	row_number::RowNumber,
12};
13use serde::{Deserialize, Serialize};
14
15use crate::interface::catalog::column::Column;
16
17#[derive(Debug, Clone)]
18pub struct Row {
19	pub number: RowNumber,
20	pub encoded: EncodedBytes,
21	pub shape: RowShape,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub struct Ttl {
26	pub duration: Duration,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub struct RowSettings {
31	pub ttl: Option<Ttl>,
32
33	pub persistent: bool,
34}
35
36impl RowSettings {
37	pub fn is_persistent(&self) -> bool {
38		self.persistent
39	}
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct OperatorSettings {
44	pub lateness: Option<OperatorLateness>,
45
46	pub join: Option<JoinLateness>,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50pub struct OperatorLateness {
51	pub duration: Duration,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub struct JoinLateness {
56	pub left: Option<OperatorLateness>,
57
58	pub right: Option<OperatorLateness>,
59}
60
61pub fn row_shape_from_columns(family: RowFamily, value: &[Column]) -> RowShape {
62	{
63		let fields = value
64			.iter()
65			.map(|col| {
66				let constraint = match col.constraint.constraint() {
67					Some(Constraint::Dictionary(dict_id, id_type)) => {
68						TypeConstraint::dictionary(*dict_id, id_type.clone())
69					}
70					_ => col.constraint.clone(),
71				};
72				RowShapeField::new(col.name.clone(), constraint)
73			})
74			.collect();
75		RowShape::new(family, fields)
76	}
77}
78
79#[cfg(test)]
80mod tests {
81	mod from_shape {
82		// Tests removed as From<&RowShape> for the old layout type has been removed
83		// RowShape is now the canonical layout descriptor
84	}
85
86	mod from_column {
87		use reifydb_codec::row::shape::{RowFamily, RowShape, RowShapeField};
88		use reifydb_value::value::{constraint::TypeConstraint, value_type::ValueType};
89
90		use crate::{
91			interface::catalog::{
92				column::{Column, ColumnIndex},
93				id::ColumnId,
94			},
95			row::row_shape_from_columns,
96		};
97
98		fn make_column(id: u64, name: &str, ty: ValueType, index: u8) -> Column {
99			Column {
100				id: ColumnId(id),
101				name: name.to_string(),
102				constraint: TypeConstraint::unconstrained(ty),
103				properties: vec![],
104				index: ColumnIndex(index),
105				auto_increment: false,
106				dictionary_id: None,
107			}
108		}
109
110		#[test]
111		fn test_from_column_single_field() {
112			let columns = vec![make_column(1, "id", ValueType::Int8, 0)];
113
114			let shape = row_shape_from_columns(RowFamily::Table, columns.as_slice());
115
116			assert_eq!(shape.fields().len(), 1);
117			assert_eq!(shape.fields()[0].name, "id");
118			assert_eq!(shape.fields()[0].constraint.get_type(), ValueType::Int8);
119		}
120
121		#[test]
122		fn test_from_column_multiple_fields() {
123			let columns = vec![
124				make_column(1, "a", ValueType::Int1, 0),
125				make_column(2, "b", ValueType::Int2, 1),
126				make_column(3, "c", ValueType::Int4, 2),
127			];
128
129			let shape = row_shape_from_columns(RowFamily::Table, columns.as_slice());
130
131			assert_eq!(shape.fields().len(), 3);
132			assert_eq!(shape.fields()[0].name, "a");
133			assert_eq!(shape.fields()[0].constraint.get_type(), ValueType::Int1);
134			assert_eq!(shape.fields()[1].name, "b");
135			assert_eq!(shape.fields()[1].constraint.get_type(), ValueType::Int2);
136			assert_eq!(shape.fields()[2].name, "c");
137			assert_eq!(shape.fields()[2].constraint.get_type(), ValueType::Int4);
138		}
139
140		#[test]
141		fn test_from_column_preserves_field_order() {
142			let columns = vec![
143				make_column(1, "first", ValueType::Utf8, 0),
144				make_column(2, "second", ValueType::Int4, 1),
145				make_column(3, "third", ValueType::Boolean, 2),
146			];
147
148			let shape = row_shape_from_columns(RowFamily::Table, columns.as_slice());
149
150			assert_eq!(shape.fields()[0].name, "first");
151			assert_eq!(shape.fields()[0].constraint.get_type(), ValueType::Utf8);
152			assert_eq!(shape.fields()[1].name, "second");
153			assert_eq!(shape.fields()[1].constraint.get_type(), ValueType::Int4);
154			assert_eq!(shape.fields()[2].name, "third");
155			assert_eq!(shape.fields()[2].constraint.get_type(), ValueType::Boolean);
156		}
157
158		#[test]
159		fn test_from_column_equivalence_with_direct_construction() {
160			let columns = vec![
161				make_column(1, "f0", ValueType::Uint1, 0),
162				make_column(2, "f1", ValueType::Uint2, 1),
163				make_column(3, "f2", ValueType::Uint4, 2),
164				make_column(4, "f3", ValueType::Uint8, 3),
165				make_column(5, "f4", ValueType::Uint16, 4),
166			];
167
168			let shape_from_columns = row_shape_from_columns(RowFamily::Table, columns.as_slice());
169			let shape_direct = RowShape::new(
170				RowFamily::Table,
171				vec![
172					RowShapeField::unconstrained("f0", ValueType::Uint1),
173					RowShapeField::unconstrained("f1", ValueType::Uint2),
174					RowShapeField::unconstrained("f2", ValueType::Uint4),
175					RowShapeField::unconstrained("f3", ValueType::Uint8),
176					RowShapeField::unconstrained("f4", ValueType::Uint16),
177				],
178			);
179
180			// Full equivalence check
181			assert_eq!(shape_from_columns.fields().len(), shape_direct.fields().len());
182			assert_eq!(shape_from_columns.fingerprint(), shape_direct.fingerprint());
183
184			for (i, (from_columns, direct)) in
185				shape_from_columns.fields().iter().zip(shape_direct.fields().iter()).enumerate()
186			{
187				assert_eq!(from_columns.name, direct.name, "name mismatch at field {}", i);
188				assert_eq!(
189					from_columns.constraint, direct.constraint,
190					"constraint mismatch at field {}",
191					i
192				);
193				assert_eq!(from_columns.offset, direct.offset, "offset mismatch at field {}", i);
194				assert_eq!(from_columns.size, direct.size, "size mismatch at field {}", i);
195			}
196		}
197
198		#[test]
199		fn test_from_column_empty() {
200			let columns: Vec<Column> = vec![];
201
202			let shape = row_shape_from_columns(RowFamily::Table, columns.as_slice());
203
204			assert_eq!(shape.fields().len(), 0);
205		}
206
207		#[test]
208		fn test_from_column_nine_fields() {
209			let columns = vec![
210				make_column(1, "f0", ValueType::Boolean, 0),
211				make_column(2, "f1", ValueType::Int1, 1),
212				make_column(3, "f2", ValueType::Int2, 2),
213				make_column(4, "f3", ValueType::Int4, 3),
214				make_column(5, "f4", ValueType::Int8, 4),
215				make_column(6, "f5", ValueType::Uint1, 5),
216				make_column(7, "f6", ValueType::Uint2, 6),
217				make_column(8, "f7", ValueType::Uint4, 7),
218				make_column(9, "f8", ValueType::Uint8, 8),
219			];
220
221			let shape = row_shape_from_columns(RowFamily::Table, columns.as_slice());
222
223			assert_eq!(shape.fields().len(), 9);
224			for (i, field) in shape.fields().iter().enumerate() {
225				assert_eq!(field.name, format!("f{}", i));
226			}
227		}
228	}
229}