reifydb_core/encoded/schema/
evolution.rs1use reifydb_type::value::constraint::TypeConstraint;
10
11use crate::encoded::schema::RowSchema;
12
13#[derive(Debug, Clone)]
15pub enum FieldMapping {
16 Direct {
18 source_index: usize,
19 },
20 UseDefault,
22 Removed,
24}
25
26#[derive(Debug)]
31pub struct SchemaResolver {
32 source: RowSchema,
34 target: RowSchema,
36 mappings: Vec<FieldMapping>,
38}
39
40impl SchemaResolver {
41 pub fn new(source: RowSchema, target: RowSchema) -> Option<Self> {
46 if source.fingerprint() == target.fingerprint() {
48 return Some(Self {
49 mappings: (0..target.field_count())
50 .map(|i| FieldMapping::Direct {
51 source_index: i,
52 })
53 .collect(),
54 source,
55 target,
56 });
57 }
58
59 let mut mappings = Vec::with_capacity(target.field_count());
60
61 for target_field in target.fields() {
62 if let Some((source_idx, source_field)) =
63 source.fields().iter().enumerate().find(|(_, f)| f.name == target_field.name)
64 {
65 if !Self::types_compatible(&source_field.constraint, &target_field.constraint) {
67 return None; }
69 mappings.push(FieldMapping::Direct {
70 source_index: source_idx,
71 });
72 } else {
73 mappings.push(FieldMapping::UseDefault);
75 }
76 }
77
78 Some(Self {
79 source,
80 target,
81 mappings,
82 })
83 }
84
85 fn types_compatible(source: &TypeConstraint, target: &TypeConstraint) -> bool {
88 let source_type = source.get_type();
89 let target_type = target.get_type();
90
91 if source_type == target_type {
92 return true;
93 }
94
95 false
98 }
99
100 pub fn source(&self) -> &RowSchema {
102 &self.source
103 }
104
105 pub fn target(&self) -> &RowSchema {
107 &self.target
108 }
109
110 pub fn mappings(&self) -> &[FieldMapping] {
112 &self.mappings
113 }
114
115 pub fn is_identity(&self) -> bool {
117 self.source.fingerprint() == self.target.fingerprint()
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use reifydb_type::value::r#type::Type;
124
125 use super::*;
126 use crate::encoded::schema::RowSchemaField;
127
128 #[test]
129 fn test_resolver_identity() {
130 let fields = vec![
131 RowSchemaField::unconstrained("a", Type::Int4),
132 RowSchemaField::unconstrained("b", Type::Utf8),
133 ];
134
135 let schema = RowSchema::new(fields);
136 let resolver = SchemaResolver::new(schema.clone(), schema.clone()).unwrap();
137
138 assert!(resolver.is_identity());
139 assert_eq!(resolver.mappings().len(), 2);
140 }
141
142 #[test]
143 fn test_resolver_added_field() {
144 let source_fields = vec![RowSchemaField::unconstrained("a", Type::Int4)];
145
146 let target_fields = vec![
147 RowSchemaField::unconstrained("a", Type::Int4),
148 RowSchemaField::unconstrained("b", Type::Utf8), ];
150
151 let source = RowSchema::new(source_fields);
152 let target = RowSchema::new(target_fields);
153
154 let resolver = SchemaResolver::new(source, target).unwrap();
155
156 assert!(!resolver.is_identity());
157 assert!(matches!(
158 resolver.mappings()[0],
159 FieldMapping::Direct {
160 source_index: 0
161 }
162 ));
163 assert!(matches!(resolver.mappings()[1], FieldMapping::UseDefault));
164 }
165
166 #[test]
167 fn test_resolver_incompatible_types() {
168 let source_fields = vec![RowSchemaField::unconstrained("a", Type::Int4)];
169 let target_fields = vec![RowSchemaField::unconstrained("a", Type::Utf8)]; let source = RowSchema::new(source_fields);
172 let target = RowSchema::new(target_fields);
173
174 assert!(SchemaResolver::new(source, target).is_none());
176 }
177}