lance_file/writer/
field_types.rs1use std::fmt;
14use std::sync::Arc;
15
16use arrow_array::{ArrayRef, RecordBatch};
17use arrow_schema::{DataType, Field as ArrowField, FieldRef, SchemaRef};
18use lance_arrow::BLOB_V2_EXT_NAME;
19use lance_core::{Error, Result, datatypes::Schema};
20
21#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct FieldTypeMismatch {
27 pub field_path: String,
30 pub expected: FieldRef,
32 pub actual: FieldRef,
35}
36
37impl fmt::Display for FieldTypeMismatch {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(
40 f,
41 "field `{}` does not match the file schema: expected {}, got {}",
42 self.field_path,
43 FieldType(&self.expected),
44 FieldType(&self.actual)
45 )
46 }
47}
48
49impl std::error::Error for FieldTypeMismatch {}
50
51struct FieldType<'a>(&'a ArrowField);
52
53impl fmt::Display for FieldType<'_> {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(f, "{}", self.0.data_type())?;
56 match (
57 self.0.extension_type_name(),
58 self.0.extension_type_metadata(),
59 ) {
60 (Some(name), Some(metadata)) => write!(f, " (extension {name}, metadata {metadata})"),
61 (Some(name), None) => write!(f, " (extension {name})"),
62 _ => write!(f, " (no extension)"),
63 }
64 }
65}
66
67pub struct ExpectedTypes {
69 fields: Vec<FieldRef>,
70 accepted: Option<SchemaRef>,
73}
74
75impl ExpectedTypes {
76 pub fn new(schema: &Schema) -> Self {
77 Self {
78 fields: schema
79 .fields
80 .iter()
81 .map(|field| Arc::new(ArrowField::from(field)))
82 .collect(),
83 accepted: None,
84 }
85 }
86
87 pub fn check_batch(&mut self, batch: &RecordBatch) -> Result<()> {
92 let batch_schema = batch.schema();
93 if self.accepted.as_ref() == Some(&batch_schema) {
94 return Ok(());
95 }
96 for (index, expected) in self.fields.iter().enumerate() {
97 let actual = batch_schema
100 .fields()
101 .get(index)
102 .filter(|actual| actual.name() == expected.name())
103 .or_else(|| {
104 let (index, _) = batch_schema.column_with_name(expected.name())?;
105 batch_schema.fields().get(index)
106 });
107 if let Some(actual) = actual {
108 check_field(expected, actual, true).map_err(into_error)?;
109 }
110 }
111 self.accepted = Some(batch_schema);
112 Ok(())
113 }
114
115 pub fn check_column(&self, index: usize, array: &ArrayRef) -> Result<()> {
121 let expected = &self.fields[index];
122 let actual = Arc::new(ArrowField::new(
123 expected.name(),
124 array.data_type().clone(),
125 true,
126 ));
127 check_field(expected, &actual, false).map_err(into_error)
128 }
129}
130
131fn into_error(mismatch: FieldTypeMismatch) -> Error {
132 Error::invalid_input_source(Box::new(mismatch))
133}
134
135fn check_field(
138 expected: &FieldRef,
139 actual: &FieldRef,
140 compare_extension: bool,
141) -> std::result::Result<(), FieldTypeMismatch> {
142 let mismatch = || FieldTypeMismatch {
143 field_path: expected.name().clone(),
144 expected: expected.clone(),
145 actual: actual.clone(),
146 };
147 if compare_extension
148 && (expected.extension_type_name() != actual.extension_type_name()
149 || expected.extension_type_metadata() != actual.extension_type_metadata())
150 {
151 return Err(mismatch());
152 }
153 let children = match (expected.data_type(), actual.data_type()) {
154 (DataType::Struct(_), DataType::Struct(_))
158 if expected.extension_type_name() == Some(BLOB_V2_EXT_NAME) =>
159 {
160 return Ok(());
161 }
162 (DataType::Struct(expected_children), DataType::Struct(actual_children))
163 if expected_children.len() == actual_children.len() =>
164 {
165 expected_children
166 .iter()
167 .zip(actual_children.iter())
168 .collect()
169 }
170 (DataType::List(expected_item), DataType::List(actual_item))
171 | (DataType::LargeList(expected_item), DataType::LargeList(actual_item))
172 | (DataType::Map(expected_item, _), DataType::Map(actual_item, _)) => {
173 vec![(expected_item, actual_item)]
174 }
175 (
176 DataType::FixedSizeList(expected_item, expected_size),
177 DataType::FixedSizeList(actual_item, actual_size),
178 ) if expected_size == actual_size => vec![(expected_item, actual_item)],
179 (DataType::Utf8, DataType::Utf8View) | (DataType::Binary, DataType::BinaryView) => {
181 return Ok(());
182 }
183 (expected_type, actual_type) if expected_type == actual_type => return Ok(()),
184 _ => return Err(mismatch()),
185 };
186 for (expected_child, actual_child) in children {
187 check_field(expected_child, actual_child, true).map_err(|mut mismatch| {
188 mismatch.field_path = format!("{}.{}", expected.name(), mismatch.field_path);
189 mismatch
190 })?;
191 }
192 Ok(())
193}