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