Skip to main content

reifydb_core/encoded/schema/
from.rs

1//  SPDX-License-Identifier: AGPL-3.0-or-later
2//  Copyright (c) 2025 ReifyDB
3
4use reifydb_type::value::constraint::{Constraint, TypeConstraint};
5
6use crate::{
7	encoded::schema::{Schema, SchemaField},
8	interface::catalog::{column::ColumnDef, subscription::SubscriptionColumnDef},
9};
10
11impl From<&Vec<ColumnDef>> for Schema {
12	fn from(value: &Vec<ColumnDef>) -> Self {
13		Schema::from(value.as_slice())
14	}
15}
16impl From<&[ColumnDef]> for Schema {
17	fn from(value: &[ColumnDef]) -> Self {
18		let fields = value
19			.iter()
20			.map(|col| {
21				// For dictionary columns, the constraint carries Dictionary info with id_type.
22				// Convert to TypeConstraint::dictionary() so the schema uses DictionaryId as base_type,
23				// matching the schema used to encode the data.
24				let constraint = match col.constraint.constraint() {
25					Some(Constraint::Dictionary(dict_id, id_type)) => {
26						TypeConstraint::dictionary(*dict_id, id_type.clone())
27					}
28					_ => col.constraint.clone(),
29				};
30				SchemaField::new(col.name.clone(), constraint)
31			})
32			.collect();
33		Schema::new(fields)
34	}
35}
36
37impl From<&Vec<SubscriptionColumnDef>> for Schema {
38	fn from(value: &Vec<SubscriptionColumnDef>) -> Self {
39		Schema::from(value.as_slice())
40	}
41}
42impl From<&[SubscriptionColumnDef]> for Schema {
43	fn from(value: &[SubscriptionColumnDef]) -> Self {
44		let fields = value
45			.iter()
46			.map(|col| SchemaField::new(col.name.clone(), TypeConstraint::unconstrained(col.ty.clone())))
47			.collect();
48		Schema::new(fields)
49	}
50}
51
52#[cfg(test)]
53mod tests {
54	mod from_schema {
55		// Tests removed as From<&Schema> for EncodedValuesLayout has been removed
56		// EncodedValuesLayout is being phased out in favor of Schema
57	}
58
59	mod from_column_def {
60		use reifydb_type::value::{constraint::TypeConstraint, r#type::Type};
61
62		use crate::{
63			encoded::schema::{Schema, SchemaField},
64			interface::catalog::{
65				column::{ColumnDef, ColumnIndex},
66				id::ColumnId,
67			},
68		};
69
70		fn make_column_def(id: u64, name: &str, ty: Type, index: u8) -> ColumnDef {
71			ColumnDef {
72				id: ColumnId(id),
73				name: name.to_string(),
74				constraint: TypeConstraint::unconstrained(ty),
75				properties: vec![],
76				index: ColumnIndex(index),
77				auto_increment: false,
78				dictionary_id: None,
79			}
80		}
81
82		#[test]
83		fn test_from_column_def_single_field() {
84			let columns = vec![make_column_def(1, "id", Type::Int8, 0)];
85
86			let schema = Schema::from(columns.as_slice());
87
88			assert_eq!(schema.fields().len(), 1);
89			assert_eq!(schema.fields()[0].name, "id");
90			assert_eq!(schema.fields()[0].constraint.get_type(), Type::Int8);
91		}
92
93		#[test]
94		fn test_from_column_def_multiple_fields() {
95			let columns = vec![
96				make_column_def(1, "a", Type::Int1, 0),
97				make_column_def(2, "b", Type::Int2, 1),
98				make_column_def(3, "c", Type::Int4, 2),
99			];
100
101			let schema = Schema::from(columns.as_slice());
102
103			assert_eq!(schema.fields().len(), 3);
104			assert_eq!(schema.fields()[0].name, "a");
105			assert_eq!(schema.fields()[0].constraint.get_type(), Type::Int1);
106			assert_eq!(schema.fields()[1].name, "b");
107			assert_eq!(schema.fields()[1].constraint.get_type(), Type::Int2);
108			assert_eq!(schema.fields()[2].name, "c");
109			assert_eq!(schema.fields()[2].constraint.get_type(), Type::Int4);
110		}
111
112		#[test]
113		fn test_from_column_def_preserves_field_order() {
114			let columns = vec![
115				make_column_def(1, "first", Type::Utf8, 0),
116				make_column_def(2, "second", Type::Int4, 1),
117				make_column_def(3, "third", Type::Boolean, 2),
118			];
119
120			let schema = Schema::from(columns.as_slice());
121
122			assert_eq!(schema.fields()[0].name, "first");
123			assert_eq!(schema.fields()[0].constraint.get_type(), Type::Utf8);
124			assert_eq!(schema.fields()[1].name, "second");
125			assert_eq!(schema.fields()[1].constraint.get_type(), Type::Int4);
126			assert_eq!(schema.fields()[2].name, "third");
127			assert_eq!(schema.fields()[2].constraint.get_type(), Type::Boolean);
128		}
129
130		#[test]
131		fn test_from_column_def_equivalence_with_direct_construction() {
132			let columns = vec![
133				make_column_def(1, "f0", Type::Uint1, 0),
134				make_column_def(2, "f1", Type::Uint2, 1),
135				make_column_def(3, "f2", Type::Uint4, 2),
136				make_column_def(4, "f3", Type::Uint8, 3),
137				make_column_def(5, "f4", Type::Uint16, 4),
138			];
139
140			let schema_from_columns = Schema::from(columns.as_slice());
141			let schema_direct = Schema::new(vec![
142				SchemaField::unconstrained("f0", Type::Uint1),
143				SchemaField::unconstrained("f1", Type::Uint2),
144				SchemaField::unconstrained("f2", Type::Uint4),
145				SchemaField::unconstrained("f3", Type::Uint8),
146				SchemaField::unconstrained("f4", Type::Uint16),
147			]);
148
149			// Full equivalence check
150			assert_eq!(schema_from_columns.fields().len(), schema_direct.fields().len());
151			assert_eq!(schema_from_columns.fingerprint(), schema_direct.fingerprint());
152
153			for (i, (from_columns, direct)) in
154				schema_from_columns.fields().iter().zip(schema_direct.fields().iter()).enumerate()
155			{
156				assert_eq!(from_columns.name, direct.name, "name mismatch at field {}", i);
157				assert_eq!(
158					from_columns.constraint, direct.constraint,
159					"constraint mismatch at field {}",
160					i
161				);
162				assert_eq!(from_columns.offset, direct.offset, "offset mismatch at field {}", i);
163				assert_eq!(from_columns.size, direct.size, "size mismatch at field {}", i);
164				assert_eq!(from_columns.align, direct.align, "align mismatch at field {}", i);
165			}
166		}
167
168		#[test]
169		fn test_from_column_def_empty() {
170			let columns: Vec<ColumnDef> = vec![];
171
172			let schema = Schema::from(columns.as_slice());
173
174			assert_eq!(schema.fields().len(), 0);
175		}
176
177		#[test]
178		fn test_from_column_def_nine_fields() {
179			let columns = vec![
180				make_column_def(1, "f0", Type::Boolean, 0),
181				make_column_def(2, "f1", Type::Int1, 1),
182				make_column_def(3, "f2", Type::Int2, 2),
183				make_column_def(4, "f3", Type::Int4, 3),
184				make_column_def(5, "f4", Type::Int8, 4),
185				make_column_def(6, "f5", Type::Uint1, 5),
186				make_column_def(7, "f6", Type::Uint2, 6),
187				make_column_def(8, "f7", Type::Uint4, 7),
188				make_column_def(9, "f8", Type::Uint8, 8),
189			];
190
191			let schema = Schema::from(columns.as_slice());
192
193			assert_eq!(schema.fields().len(), 9);
194			for (i, field) in schema.fields().iter().enumerate() {
195				assert_eq!(field.name, format!("f{}", i));
196			}
197		}
198	}
199}