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