1use std::collections::{HashMap, HashSet};
21use std::fmt::{Display, Formatter};
22use std::sync::Arc;
23
24mod utils;
25mod visitor;
26pub use self::visitor::*;
27pub(super) mod _serde;
28mod id_reassigner;
29mod index;
30mod prune_columns;
31use bimap::BiHashMap;
32use itertools::{Itertools, zip_eq};
33use serde::{Deserialize, Serialize};
34
35use self::_serde::SchemaEnum;
36use self::id_reassigner::ReassignFieldIds;
37use self::index::{IndexByName, index_by_id, index_parents};
38pub use self::prune_columns::prune_columns;
39use super::NestedField;
40use crate::error::{Result, invalid_data};
41use crate::expr::accessor::StructAccessor;
42use crate::spec::FormatVersion;
43use crate::spec::datatypes::{
44 LIST_FIELD_NAME, ListType, MAP_KEY_FIELD_NAME, MAP_VALUE_FIELD_NAME, MapType, NestedFieldRef,
45 PrimitiveType, StructType, Type,
46};
47use crate::{Error, ErrorKind, ensure_data_valid};
48
49pub type SchemaId = i32;
51pub type SchemaRef = Arc<Schema>;
53pub const DEFAULT_SCHEMA_ID: SchemaId = 0;
55pub const SCHEMA_NAME_DELIMITER: &str = ".";
57pub(crate) const DEFAULT_VALUES_MIN_FORMAT_VERSION: FormatVersion = FormatVersion::V3;
60
61#[derive(Debug, Serialize, Deserialize, Clone)]
63#[serde(try_from = "SchemaEnum", into = "SchemaEnum")]
64pub struct Schema {
65 r#struct: StructType,
66 schema_id: SchemaId,
67 highest_field_id: i32,
68 identifier_field_ids: HashSet<i32>,
69
70 alias_to_id: BiHashMap<String, i32>,
71 id_to_field: HashMap<i32, NestedFieldRef>,
72
73 name_to_id: HashMap<String, i32>,
74 lowercase_name_to_id: HashMap<String, i32>,
75 id_to_name: HashMap<i32, String>,
76
77 field_id_to_accessor: HashMap<i32, Arc<StructAccessor>>,
78}
79
80impl PartialEq for Schema {
81 fn eq(&self, other: &Self) -> bool {
82 self.r#struct == other.r#struct
83 && self.schema_id == other.schema_id
84 && self.identifier_field_ids == other.identifier_field_ids
85 }
86}
87
88impl Eq for Schema {}
89
90#[derive(Debug)]
92pub struct SchemaBuilder {
93 schema_id: i32,
94 fields: Vec<NestedFieldRef>,
95 alias_to_id: BiHashMap<String, i32>,
96 identifier_field_ids: HashSet<i32>,
97 reassign_field_ids_from: Option<i32>,
98}
99
100impl SchemaBuilder {
101 pub fn with_fields(mut self, fields: impl IntoIterator<Item = NestedFieldRef>) -> Self {
103 self.fields.extend(fields);
104 self
105 }
106
107 pub(crate) fn with_reassigned_field_ids(mut self, start_from: i32) -> Self {
112 self.reassign_field_ids_from = Some(start_from);
113 self
114 }
115
116 pub fn with_schema_id(mut self, schema_id: i32) -> Self {
118 self.schema_id = schema_id;
119 self
120 }
121
122 pub fn with_identifier_field_ids(mut self, ids: impl IntoIterator<Item = i32>) -> Self {
124 self.identifier_field_ids.extend(ids);
125 self
126 }
127
128 pub fn with_alias(mut self, alias_to_id: BiHashMap<String, i32>) -> Self {
130 self.alias_to_id = alias_to_id;
131 self
132 }
133
134 pub fn build(self) -> Result<Schema> {
136 let field_id_to_accessor = self.build_accessors();
137
138 let r#struct = StructType::new(self.fields);
139 let id_to_field = index_by_id(&r#struct)?;
140
141 Self::validate_identifier_ids(
142 &r#struct,
143 &id_to_field,
144 self.identifier_field_ids.iter().copied(),
145 )?;
146
147 let (name_to_id, id_to_name) = {
148 let mut index = IndexByName::default();
149 visit_struct(&r#struct, &mut index)?;
150 index.indexes()
151 };
152
153 let lowercase_name_to_id = name_to_id
154 .iter()
155 .map(|(k, v)| (k.to_lowercase(), *v))
156 .collect();
157
158 let highest_field_id = id_to_field.keys().max().cloned().unwrap_or(0);
159
160 let mut schema = Schema {
161 r#struct,
162 schema_id: self.schema_id,
163 highest_field_id,
164 identifier_field_ids: self.identifier_field_ids,
165 alias_to_id: self.alias_to_id,
166 id_to_field,
167
168 name_to_id,
169 lowercase_name_to_id,
170 id_to_name,
171
172 field_id_to_accessor,
173 };
174
175 if let Some(start_from) = self.reassign_field_ids_from {
176 let mut id_reassigner = ReassignFieldIds::new(start_from);
177 let new_fields = id_reassigner.reassign_field_ids(schema.r#struct.fields().to_vec())?;
178 let new_identifier_field_ids =
179 id_reassigner.apply_to_identifier_fields(schema.identifier_field_ids)?;
180 let new_alias_to_id = id_reassigner.apply_to_aliases(schema.alias_to_id.clone())?;
181
182 schema = Schema::builder()
183 .with_schema_id(schema.schema_id)
184 .with_fields(new_fields)
185 .with_identifier_field_ids(new_identifier_field_ids)
186 .with_alias(new_alias_to_id)
187 .build()?;
188 }
189
190 Ok(schema)
191 }
192
193 fn build_accessors(&self) -> HashMap<i32, Arc<StructAccessor>> {
194 let mut map = HashMap::new();
195
196 for (pos, field) in self.fields.iter().enumerate() {
197 match field.field_type.as_ref() {
198 Type::Primitive(prim_type) => {
199 let accessor = Arc::new(StructAccessor::new(pos, prim_type.clone()));
201 map.insert(field.id, accessor.clone());
202 }
203
204 Type::Struct(nested) => {
205 for (field_id, accessor) in Self::build_accessors_nested(nested.fields()) {
207 let new_accessor = Arc::new(StructAccessor::wrap(pos, accessor));
208 map.insert(field_id, new_accessor.clone());
209 }
210 }
211 _ => {
212 }
214 }
215 }
216
217 map
218 }
219
220 fn build_accessors_nested(fields: &[NestedFieldRef]) -> Vec<(i32, Box<StructAccessor>)> {
221 let mut results = vec![];
222 for (pos, field) in fields.iter().enumerate() {
223 match field.field_type.as_ref() {
224 Type::Primitive(prim_type) => {
225 let accessor = Box::new(StructAccessor::new(pos, prim_type.clone()));
226 results.push((field.id, accessor));
227 }
228 Type::Struct(nested) => {
229 let nested_accessors = Self::build_accessors_nested(nested.fields());
230
231 let wrapped_nested_accessors =
232 nested_accessors.into_iter().map(|(id, accessor)| {
233 let new_accessor = Box::new(StructAccessor::wrap(pos, accessor));
234 (id, new_accessor.clone())
235 });
236
237 results.extend(wrapped_nested_accessors);
238 }
239 _ => {
240 }
242 }
243 }
244
245 results
246 }
247
248 fn validate_identifier_ids(
254 r#struct: &StructType,
255 id_to_field: &HashMap<i32, NestedFieldRef>,
256 identifier_field_ids: impl Iterator<Item = i32>,
257 ) -> Result<()> {
258 let id_to_parent = index_parents(r#struct)?;
259 for identifier_field_id in identifier_field_ids {
260 let field = id_to_field.get(&identifier_field_id).ok_or_else(|| {
261 invalid_data!(
262 "Cannot add identifier field {identifier_field_id}: field does not exist"
263 )
264 })?;
265 ensure_data_valid!(
266 field.required,
267 "Cannot add identifier field: {} is an optional field",
268 field.name
269 );
270 if let Type::Primitive(p) = field.field_type.as_ref() {
271 ensure_data_valid!(
272 !matches!(p, PrimitiveType::Double | PrimitiveType::Float),
273 "Cannot add identifier field {}: cannot be a float or double type",
274 field.name
275 );
276 } else {
277 return Err(invalid_data!(
278 "Cannot add field {} as an identifier field: not a primitive type field",
279 field.name
280 ));
281 }
282
283 let mut cur_field_id = identifier_field_id;
284 while let Some(parent) = id_to_parent.get(&cur_field_id) {
285 let parent_field = id_to_field
286 .get(parent)
287 .expect("Field id should not disappear.");
288 ensure_data_valid!(
289 parent_field.field_type.is_struct(),
290 "Cannot add field {} as an identifier field: must not be nested in {:?}",
291 field.name,
292 parent_field
293 );
294 ensure_data_valid!(
295 parent_field.required,
296 "Cannot add field {} as an identifier field: must not be nested in an optional field {}",
297 field.name,
298 parent_field
299 );
300 cur_field_id = *parent;
301 }
302 }
303
304 Ok(())
305 }
306}
307
308impl Schema {
309 pub fn builder() -> SchemaBuilder {
311 SchemaBuilder {
312 schema_id: DEFAULT_SCHEMA_ID,
313 fields: vec![],
314 identifier_field_ids: HashSet::default(),
315 alias_to_id: BiHashMap::default(),
316 reassign_field_ids_from: None,
317 }
318 }
319
320 pub fn into_builder(self) -> SchemaBuilder {
322 SchemaBuilder {
323 schema_id: self.schema_id,
324 fields: self.r#struct.fields().to_vec(),
325 alias_to_id: self.alias_to_id,
326 identifier_field_ids: self.identifier_field_ids,
327 reassign_field_ids_from: None,
328 }
329 }
330
331 pub fn field_by_id(&self, field_id: i32) -> Option<&NestedFieldRef> {
333 self.id_to_field.get(&field_id)
334 }
335
336 pub fn field_by_name(&self, field_name: &str) -> Option<&NestedFieldRef> {
340 self.name_to_id
341 .get(field_name)
342 .and_then(|id| self.field_by_id(*id))
343 }
344
345 pub fn field_by_name_case_insensitive(&self, field_name: &str) -> Option<&NestedFieldRef> {
349 self.lowercase_name_to_id
350 .get(&field_name.to_lowercase())
351 .and_then(|id| self.field_by_id(*id))
352 }
353
354 pub fn field_by_alias(&self, alias: &str) -> Option<&NestedFieldRef> {
356 self.alias_to_id
357 .get_by_left(alias)
358 .and_then(|id| self.field_by_id(*id))
359 }
360
361 #[inline]
363 pub fn highest_field_id(&self) -> i32 {
364 self.highest_field_id
365 }
366
367 #[inline]
369 pub fn schema_id(&self) -> SchemaId {
370 self.schema_id
371 }
372
373 #[inline]
375 pub fn as_struct(&self) -> &StructType {
376 &self.r#struct
377 }
378
379 #[inline]
381 pub fn identifier_field_ids(&self) -> impl ExactSizeIterator<Item = i32> + '_ {
382 self.identifier_field_ids.iter().copied()
383 }
384
385 pub fn field_id_by_name(&self, name: &str) -> Option<i32> {
387 self.name_to_id.get(name).copied()
388 }
389
390 pub fn name_by_field_id(&self, field_id: i32) -> Option<&str> {
392 self.id_to_name.get(&field_id).map(String::as_str)
393 }
394
395 pub fn accessor_by_field_id(&self, field_id: i32) -> Option<Arc<StructAccessor>> {
397 self.field_id_to_accessor.get(&field_id).cloned()
398 }
399
400 pub(crate) fn is_same_schema(&self, other: &SchemaRef) -> bool {
402 self.as_struct().eq(other.as_struct())
403 && self.identifier_field_ids().eq(other.identifier_field_ids())
404 }
405
406 pub(crate) fn with_schema_id(self, schema_id: SchemaId) -> Self {
410 Self { schema_id, ..self }
411 }
412
413 pub fn field_id_to_name_map(&self) -> &HashMap<i32, String> {
415 &self.id_to_name
416 }
417
418 pub fn field_id_to_fields(&self) -> &HashMap<i32, NestedFieldRef> {
420 &self.id_to_field
421 }
422
423 pub fn calc_min_compatible_format(&self) -> FormatVersion {
429 self.id_to_field
431 .values()
432 .map(|f| f.field_type.min_format_version())
433 .max()
434 .unwrap_or(FormatVersion::V1)
435 }
436
437 pub fn check_format_compatibility(&self, format_version: FormatVersion) -> Result<()> {
446 let mut problems: Vec<(i32, String)> = Vec::new();
448
449 for field in self.id_to_field.values() {
452 let min_version = field.field_type.min_format_version();
453 if format_version < min_version {
454 let name = self.name_by_field_id(field.id).ok_or_else(|| {
458 Error::new(
459 ErrorKind::Unexpected,
460 format!(
461 "Field id {} is missing from the schema's name index",
462 field.id
463 ),
464 )
465 })?;
466 problems.push((field.id, format!(
467 "Invalid type for {name}: {} is not supported until {min_version} but format version is {format_version}.",
468 field.field_type,
469 )));
470 }
471
472 if let Some(default) = &field.initial_default
473 && format_version < DEFAULT_VALUES_MIN_FORMAT_VERSION
474 {
475 let name = self.name_by_field_id(field.id).ok_or_else(|| {
476 Error::new(
477 ErrorKind::Unexpected,
478 format!(
479 "Field id {} is missing from the schema's name index",
480 field.id
481 ),
482 )
483 })?;
484 problems.push((field.id, format!(
485 "Invalid initial default for {name}: non-null default ({default:?}) is not supported until {DEFAULT_VALUES_MIN_FORMAT_VERSION} but format version is {format_version}."
486 )));
487 }
488 }
489
490 if problems.is_empty() {
491 return Ok(());
492 }
493
494 let message = problems
497 .into_iter()
498 .sorted_by_key(|(id, _)| *id)
499 .map(|(_, msg)| msg)
500 .join("\n- ");
501 Err(invalid_data!(
502 "Invalid schema for {format_version}:\n- {message}"
503 ))
504 }
505}
506
507impl Display for Schema {
508 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
509 writeln!(f, "table {{")?;
510 for field in self.as_struct().fields() {
511 writeln!(f, " {field}")?;
512 }
513 writeln!(f, "}}")
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use std::collections::HashMap;
520
521 use bimap::BiHashMap;
522
523 use crate::spec::datatypes::Type::{List, Map, Primitive, Struct, Variant};
524 use crate::spec::datatypes::{
525 ListType, MapType, NestedField, NestedFieldRef, PrimitiveType, StructType,
526 };
527 use crate::spec::schema::Schema;
528 use crate::spec::values::Map as MapValue;
529 use crate::spec::{Datum, Literal};
530
531 #[test]
532 fn test_check_format_compatibility() {
533 use crate::spec::{FormatVersion, PrimitiveLiteral, VariantType};
534
535 fn schema_with(fields: Vec<NestedFieldRef>) -> Schema {
536 Schema::builder().with_fields(fields).build().unwrap()
537 }
538
539 let variant = schema_with(vec![
541 NestedField::optional(1, "v", Variant(VariantType)).into(),
542 ]);
543 assert!(
544 variant
545 .check_format_compatibility(FormatVersion::V2)
546 .is_err()
547 );
548 assert!(
549 variant
550 .check_format_compatibility(FormatVersion::V3)
551 .is_ok()
552 );
553
554 let with_default = schema_with(vec![
556 NestedField::optional(1, "a", Primitive(PrimitiveType::Int))
557 .with_initial_default(Literal::Primitive(PrimitiveLiteral::Int(1)))
558 .into(),
559 ]);
560 let err = with_default
561 .check_format_compatibility(FormatVersion::V2)
562 .unwrap_err();
563 assert!(
564 err.message().contains("Invalid initial default for a"),
565 "{err}"
566 );
567 assert!(
568 with_default
569 .check_format_compatibility(FormatVersion::V3)
570 .is_ok()
571 );
572
573 let no_default = schema_with(vec![
575 NestedField::optional(1, "a", Primitive(PrimitiveType::Int)).into(),
576 ]);
577 assert!(
578 no_default
579 .check_format_compatibility(FormatVersion::V1)
580 .is_ok()
581 );
582
583 let nested = schema_with(vec![
585 NestedField::required(
586 1,
587 "s",
588 Struct(StructType::new(vec![
589 NestedField::optional(2, "inner", Primitive(PrimitiveType::Long))
590 .with_initial_default(Literal::Primitive(PrimitiveLiteral::Long(7)))
591 .into(),
592 ])),
593 )
594 .into(),
595 ]);
596 let err = nested
597 .check_format_compatibility(FormatVersion::V2)
598 .unwrap_err();
599 assert!(err.message().contains("inner"), "{err}");
600
601 let nested_variant = schema_with(vec![
603 NestedField::required(
604 1,
605 "container",
606 Struct(StructType::new(vec![
607 NestedField::optional(2, "v", Variant(VariantType)).into(),
608 ])),
609 )
610 .into(),
611 ]);
612 let err = nested_variant
613 .check_format_compatibility(FormatVersion::V2)
614 .unwrap_err();
615 assert!(err.message().contains("container.v"), "{err}");
616 assert!(
617 !err.message().contains("Invalid type for container:"),
618 "container must not be blamed: {err}"
619 );
620 }
621
622 #[test]
623 fn test_calc_min_compatible_format() {
624 use crate::spec::{FormatVersion, VariantType};
625
626 fn schema_with(fields: Vec<NestedFieldRef>) -> Schema {
627 Schema::builder().with_fields(fields).build().unwrap()
628 }
629
630 let v1 = schema_with(vec![
632 NestedField::required(1, "a", Primitive(PrimitiveType::Int)).into(),
633 NestedField::optional(2, "b", Primitive(PrimitiveType::String)).into(),
634 ]);
635 assert_eq!(v1.calc_min_compatible_format(), FormatVersion::V1);
636
637 let variant = schema_with(vec![
639 NestedField::optional(1, "v", Variant(VariantType)).into(),
640 ]);
641 assert_eq!(variant.calc_min_compatible_format(), FormatVersion::V3);
642
643 let unknown = schema_with(vec![
645 NestedField::optional(1, "u", Primitive(PrimitiveType::Unknown)).into(),
646 ]);
647 assert_eq!(unknown.calc_min_compatible_format(), FormatVersion::V3);
648 assert!(
649 unknown
650 .check_format_compatibility(FormatVersion::V2)
651 .is_err()
652 );
653
654 let nested = schema_with(vec![
656 NestedField::required(
657 1,
658 "s",
659 Struct(StructType::new(vec![
660 NestedField::optional(
661 2,
662 "l",
663 List(ListType::new(
664 NestedField::required(
665 3,
666 "element",
667 Primitive(PrimitiveType::TimestampNs),
668 )
669 .into(),
670 )),
671 )
672 .into(),
673 ])),
674 )
675 .into(),
676 ]);
677 assert_eq!(nested.calc_min_compatible_format(), FormatVersion::V3);
678 }
679
680 #[test]
681 fn test_check_format_compatibility_message_order() {
682 use crate::spec::{FormatVersion, PrimitiveLiteral, VariantType};
683
684 let schema = Schema::builder()
688 .with_fields(vec![
689 NestedField::optional(3, "c", Variant(VariantType)).into(),
690 NestedField::optional(2, "b", Primitive(PrimitiveType::TimestampNs))
691 .with_initial_default(Literal::Primitive(PrimitiveLiteral::Long(0)))
692 .into(),
693 NestedField::required(1, "a", Primitive(PrimitiveType::Int)).into(),
694 ])
695 .build()
696 .unwrap();
697
698 let message = schema
699 .check_format_compatibility(FormatVersion::V2)
700 .unwrap_err()
701 .message()
702 .to_string();
703
704 let lines: Vec<&str> = message.lines().skip(1).collect();
705 assert_eq!(
706 lines,
707 vec![
708 "- Invalid type for b: timestamp_ns is not supported until v3 but format version is v2.",
709 "- Invalid initial default for b: non-null default (Primitive(Long(0))) is not supported until v3 but format version is v2.",
710 "- Invalid type for c: variant is not supported until v3 but format version is v2.",
711 ],
712 "{message}"
713 );
714 }
715
716 #[test]
717 fn test_construct_schema() {
718 let field1: NestedFieldRef =
719 NestedField::required(1, "f1", Primitive(PrimitiveType::Boolean)).into();
720 let field2: NestedFieldRef =
721 NestedField::optional(2, "f2", Primitive(PrimitiveType::Int)).into();
722
723 let schema = Schema::builder()
724 .with_fields(vec![field1.clone()])
725 .with_fields(vec![field2.clone()])
726 .with_schema_id(3)
727 .build()
728 .unwrap();
729
730 assert_eq!(3, schema.schema_id());
731 assert_eq!(2, schema.highest_field_id());
732 assert_eq!(Some(&field1), schema.field_by_id(1));
733 assert_eq!(Some(&field2), schema.field_by_id(2));
734 assert_eq!(None, schema.field_by_id(3));
735 }
736
737 pub fn table_schema_simple<'a>() -> (Schema, &'a str) {
738 let schema = Schema::builder()
739 .with_schema_id(1)
740 .with_identifier_field_ids(vec![2])
741 .with_fields(vec![
742 NestedField::optional(1, "foo", Primitive(PrimitiveType::String)).into(),
743 NestedField::required(2, "bar", Primitive(PrimitiveType::Int)).into(),
744 NestedField::optional(3, "baz", Primitive(PrimitiveType::Boolean)).into(),
745 ])
746 .build()
747 .unwrap();
748 let record = r#"{
749 "type":"struct",
750 "schema-id":1,
751 "fields":[
752 {
753 "id":1,
754 "name":"foo",
755 "required":false,
756 "type":"string"
757 },
758 {
759 "id":2,
760 "name":"bar",
761 "required":true,
762 "type":"int"
763 },
764 {
765 "id":3,
766 "name":"baz",
767 "required":false,
768 "type":"boolean"
769 }
770 ],
771 "identifier-field-ids":[2]
772 }"#;
773 (schema, record)
774 }
775
776 pub fn table_schema_nested() -> Schema {
777 Schema::builder()
778 .with_schema_id(1)
779 .with_identifier_field_ids(vec![2])
780 .with_fields(vec![
781 NestedField::optional(1, "foo", Primitive(PrimitiveType::String)).into(),
782 NestedField::required(2, "bar", Primitive(PrimitiveType::Int)).into(),
783 NestedField::optional(3, "baz", Primitive(PrimitiveType::Boolean)).into(),
784 NestedField::required(
785 4,
786 "qux",
787 List(ListType {
788 element_field: NestedField::list_element(
789 5,
790 Primitive(PrimitiveType::String),
791 true,
792 )
793 .into(),
794 }),
795 )
796 .into(),
797 NestedField::required(
798 6,
799 "quux",
800 Map(MapType {
801 key_field: NestedField::map_key_element(
802 7,
803 Primitive(PrimitiveType::String),
804 )
805 .into(),
806 value_field: NestedField::map_value_element(
807 8,
808 Map(MapType {
809 key_field: NestedField::map_key_element(
810 9,
811 Primitive(PrimitiveType::String),
812 )
813 .into(),
814 value_field: NestedField::map_value_element(
815 10,
816 Primitive(PrimitiveType::Int),
817 true,
818 )
819 .into(),
820 }),
821 true,
822 )
823 .into(),
824 }),
825 )
826 .into(),
827 NestedField::required(
828 11,
829 "location",
830 List(ListType {
831 element_field: NestedField::list_element(
832 12,
833 Struct(StructType::new(vec![
834 NestedField::optional(
835 13,
836 "latitude",
837 Primitive(PrimitiveType::Float),
838 )
839 .into(),
840 NestedField::optional(
841 14,
842 "longitude",
843 Primitive(PrimitiveType::Float),
844 )
845 .into(),
846 ])),
847 true,
848 )
849 .into(),
850 }),
851 )
852 .into(),
853 NestedField::optional(
854 15,
855 "person",
856 Struct(StructType::new(vec![
857 NestedField::optional(16, "name", Primitive(PrimitiveType::String)).into(),
858 NestedField::required(17, "age", Primitive(PrimitiveType::Int)).into(),
859 ])),
860 )
861 .into(),
862 ])
863 .build()
864 .unwrap()
865 }
866
867 #[test]
868 fn test_schema_display() {
869 let expected_str = "
870table {
871 1: foo: optional string\x20
872 2: bar: required int\x20
873 3: baz: optional boolean\x20
874}
875";
876
877 assert_eq!(expected_str, format!("\n{}", table_schema_simple().0));
878 }
879
880 #[test]
881 fn test_schema_build_failed_on_duplicate_names() {
882 let ret = Schema::builder()
883 .with_schema_id(1)
884 .with_identifier_field_ids(vec![1])
885 .with_fields(vec![
886 NestedField::required(1, "foo", Primitive(PrimitiveType::String)).into(),
887 NestedField::required(2, "bar", Primitive(PrimitiveType::Int)).into(),
888 NestedField::optional(3, "baz", Primitive(PrimitiveType::Boolean)).into(),
889 NestedField::optional(4, "baz", Primitive(PrimitiveType::Boolean)).into(),
890 ])
891 .build();
892
893 assert!(
894 ret.unwrap_err()
895 .message()
896 .contains("Invalid schema: multiple fields for name baz")
897 );
898 }
899
900 #[test]
901 fn test_schema_into_builder() {
902 let original_schema = table_schema_nested();
903 let builder = original_schema.clone().into_builder();
904 let schema = builder.build().unwrap();
905
906 assert_eq!(original_schema, schema);
907 }
908
909 #[test]
910 fn test_schema_index_by_name() {
911 let expected_name_to_id = HashMap::from(
912 [
913 ("foo", 1),
914 ("bar", 2),
915 ("baz", 3),
916 ("qux", 4),
917 ("qux.element", 5),
918 ("quux", 6),
919 ("quux.key", 7),
920 ("quux.value", 8),
921 ("quux.value.key", 9),
922 ("quux.value.value", 10),
923 ("location", 11),
924 ("location.element", 12),
925 ("location.element.latitude", 13),
926 ("location.element.longitude", 14),
927 ("location.latitude", 13),
928 ("location.longitude", 14),
929 ("person", 15),
930 ("person.name", 16),
931 ("person.age", 17),
932 ]
933 .map(|e| (e.0.to_string(), e.1)),
934 );
935
936 let schema = table_schema_nested();
937 assert_eq!(&expected_name_to_id, &schema.name_to_id);
938 }
939
940 #[test]
941 fn test_schema_index_by_name_case_insensitive() {
942 let expected_name_to_id = HashMap::from(
943 [
944 ("fOo", 1),
945 ("Bar", 2),
946 ("BAz", 3),
947 ("quX", 4),
948 ("quX.ELEment", 5),
949 ("qUUx", 6),
950 ("QUUX.KEY", 7),
951 ("QUUX.Value", 8),
952 ("qUUX.VALUE.Key", 9),
953 ("qUux.VaLue.Value", 10),
954 ("lOCAtION", 11),
955 ("LOCAtioN.ELeMENt", 12),
956 ("LoCATion.element.LATitude", 13),
957 ("locatION.ElemeNT.LONgitude", 14),
958 ("LOCAtiON.LATITUDE", 13),
959 ("LOCATION.LONGITUDE", 14),
960 ("PERSon", 15),
961 ("PERSON.Name", 16),
962 ("peRSON.AGe", 17),
963 ]
964 .map(|e| (e.0.to_string(), e.1)),
965 );
966
967 let schema = table_schema_nested();
968 for (name, id) in expected_name_to_id {
969 assert_eq!(
970 Some(id),
971 schema.field_by_name_case_insensitive(&name).map(|f| f.id)
972 );
973 }
974 }
975
976 #[test]
977 fn test_schema_find_column_name() {
978 let expected_column_name = HashMap::from([
979 (1, "foo"),
980 (2, "bar"),
981 (3, "baz"),
982 (4, "qux"),
983 (5, "qux.element"),
984 (6, "quux"),
985 (7, "quux.key"),
986 (8, "quux.value"),
987 (9, "quux.value.key"),
988 (10, "quux.value.value"),
989 (11, "location"),
990 (12, "location.element"),
991 (13, "location.element.latitude"),
992 (14, "location.element.longitude"),
993 ]);
994
995 let schema = table_schema_nested();
996 for (id, name) in expected_column_name {
997 assert_eq!(
998 Some(name),
999 schema.name_by_field_id(id),
1000 "Column name for field id {id} not match."
1001 );
1002 }
1003 }
1004
1005 #[test]
1006 fn test_schema_find_column_name_not_found() {
1007 let schema = table_schema_nested();
1008
1009 assert!(schema.name_by_field_id(99).is_none());
1010 }
1011
1012 #[test]
1013 fn test_schema_find_column_name_by_id_simple() {
1014 let expected_id_to_name = HashMap::from([(1, "foo"), (2, "bar"), (3, "baz")]);
1015
1016 let schema = table_schema_simple().0;
1017
1018 for (id, name) in expected_id_to_name {
1019 assert_eq!(
1020 Some(name),
1021 schema.name_by_field_id(id),
1022 "Column name for field id {id} not match."
1023 );
1024 }
1025 }
1026
1027 #[test]
1028 fn test_schema_find_simple() {
1029 let schema = table_schema_simple().0;
1030
1031 assert_eq!(
1032 Some(schema.r#struct.fields()[0].clone()),
1033 schema.field_by_id(1).cloned()
1034 );
1035 assert_eq!(
1036 Some(schema.r#struct.fields()[1].clone()),
1037 schema.field_by_id(2).cloned()
1038 );
1039 assert_eq!(
1040 Some(schema.r#struct.fields()[2].clone()),
1041 schema.field_by_id(3).cloned()
1042 );
1043
1044 assert!(schema.field_by_id(4).is_none());
1045 assert!(schema.field_by_name("non exist").is_none());
1046 }
1047
1048 #[test]
1049 fn test_schema_find_nested() {
1050 let expected_id_to_field: HashMap<i32, NestedField> = HashMap::from([
1051 (
1052 1,
1053 NestedField::optional(1, "foo", Primitive(PrimitiveType::String)),
1054 ),
1055 (
1056 2,
1057 NestedField::required(2, "bar", Primitive(PrimitiveType::Int)),
1058 ),
1059 (
1060 3,
1061 NestedField::optional(3, "baz", Primitive(PrimitiveType::Boolean)),
1062 ),
1063 (
1064 4,
1065 NestedField::required(
1066 4,
1067 "qux",
1068 List(ListType {
1069 element_field: NestedField::list_element(
1070 5,
1071 Primitive(PrimitiveType::String),
1072 true,
1073 )
1074 .into(),
1075 }),
1076 ),
1077 ),
1078 (
1079 5,
1080 NestedField::required(5, "element", Primitive(PrimitiveType::String)),
1081 ),
1082 (
1083 6,
1084 NestedField::required(
1085 6,
1086 "quux",
1087 Map(MapType {
1088 key_field: NestedField::map_key_element(
1089 7,
1090 Primitive(PrimitiveType::String),
1091 )
1092 .into(),
1093 value_field: NestedField::map_value_element(
1094 8,
1095 Map(MapType {
1096 key_field: NestedField::map_key_element(
1097 9,
1098 Primitive(PrimitiveType::String),
1099 )
1100 .into(),
1101 value_field: NestedField::map_value_element(
1102 10,
1103 Primitive(PrimitiveType::Int),
1104 true,
1105 )
1106 .into(),
1107 }),
1108 true,
1109 )
1110 .into(),
1111 }),
1112 ),
1113 ),
1114 (
1115 7,
1116 NestedField::required(7, "key", Primitive(PrimitiveType::String)),
1117 ),
1118 (
1119 8,
1120 NestedField::required(
1121 8,
1122 "value",
1123 Map(MapType {
1124 key_field: NestedField::map_key_element(
1125 9,
1126 Primitive(PrimitiveType::String),
1127 )
1128 .into(),
1129 value_field: NestedField::map_value_element(
1130 10,
1131 Primitive(PrimitiveType::Int),
1132 true,
1133 )
1134 .into(),
1135 }),
1136 ),
1137 ),
1138 (
1139 9,
1140 NestedField::required(9, "key", Primitive(PrimitiveType::String)),
1141 ),
1142 (
1143 10,
1144 NestedField::required(10, "value", Primitive(PrimitiveType::Int)),
1145 ),
1146 (
1147 11,
1148 NestedField::required(
1149 11,
1150 "location",
1151 List(ListType {
1152 element_field: NestedField::list_element(
1153 12,
1154 Struct(StructType::new(vec![
1155 NestedField::optional(
1156 13,
1157 "latitude",
1158 Primitive(PrimitiveType::Float),
1159 )
1160 .into(),
1161 NestedField::optional(
1162 14,
1163 "longitude",
1164 Primitive(PrimitiveType::Float),
1165 )
1166 .into(),
1167 ])),
1168 true,
1169 )
1170 .into(),
1171 }),
1172 ),
1173 ),
1174 (
1175 12,
1176 NestedField::list_element(
1177 12,
1178 Struct(StructType::new(vec![
1179 NestedField::optional(13, "latitude", Primitive(PrimitiveType::Float))
1180 .into(),
1181 NestedField::optional(14, "longitude", Primitive(PrimitiveType::Float))
1182 .into(),
1183 ])),
1184 true,
1185 ),
1186 ),
1187 (
1188 13,
1189 NestedField::optional(13, "latitude", Primitive(PrimitiveType::Float)),
1190 ),
1191 (
1192 14,
1193 NestedField::optional(14, "longitude", Primitive(PrimitiveType::Float)),
1194 ),
1195 (
1196 15,
1197 NestedField::optional(
1198 15,
1199 "person",
1200 Struct(StructType::new(vec![
1201 NestedField::optional(16, "name", Primitive(PrimitiveType::String)).into(),
1202 NestedField::required(17, "age", Primitive(PrimitiveType::Int)).into(),
1203 ])),
1204 ),
1205 ),
1206 (
1207 16,
1208 NestedField::optional(16, "name", Primitive(PrimitiveType::String)),
1209 ),
1210 (
1211 17,
1212 NestedField::required(17, "age", Primitive(PrimitiveType::Int)),
1213 ),
1214 ]);
1215
1216 let schema = table_schema_nested();
1217 for (id, field) in expected_id_to_field {
1218 assert_eq!(
1219 Some(&field),
1220 schema.field_by_id(id).map(|f| f.as_ref()),
1221 "Field for {id} not match."
1222 );
1223 }
1224 }
1225
1226 #[test]
1227 fn test_build_accessors() {
1228 let schema = table_schema_nested();
1229
1230 let test_struct = crate::spec::Struct::from_iter(vec![
1231 Some(Literal::string("foo value")),
1232 Some(Literal::int(1002)),
1233 Some(Literal::bool(true)),
1234 Some(Literal::List(vec![
1235 Some(Literal::string("qux item 1")),
1236 Some(Literal::string("qux item 2")),
1237 ])),
1238 Some(Literal::Map(MapValue::from([(
1239 Literal::string("quux key 1"),
1240 Some(Literal::Map(MapValue::from([(
1241 Literal::string("quux nested key 1"),
1242 Some(Literal::int(1000)),
1243 )]))),
1244 )]))),
1245 Some(Literal::List(vec![Some(Literal::Struct(
1246 crate::spec::Struct::from_iter(vec![
1247 Some(Literal::float(52.509_09_f32)),
1248 Some(Literal::float(-1.885_249_f32)),
1249 ]),
1250 ))])),
1251 Some(Literal::Struct(crate::spec::Struct::from_iter(vec![
1252 Some(Literal::string("Testy McTest")),
1253 Some(Literal::int(33)),
1254 ]))),
1255 ]);
1256
1257 assert_eq!(
1258 schema
1259 .accessor_by_field_id(1)
1260 .unwrap()
1261 .get(&test_struct)
1262 .unwrap(),
1263 Some(Datum::string("foo value"))
1264 );
1265 assert_eq!(
1266 schema
1267 .accessor_by_field_id(2)
1268 .unwrap()
1269 .get(&test_struct)
1270 .unwrap(),
1271 Some(Datum::int(1002))
1272 );
1273 assert_eq!(
1274 schema
1275 .accessor_by_field_id(3)
1276 .unwrap()
1277 .get(&test_struct)
1278 .unwrap(),
1279 Some(Datum::bool(true))
1280 );
1281 assert_eq!(
1282 schema
1283 .accessor_by_field_id(16)
1284 .unwrap()
1285 .get(&test_struct)
1286 .unwrap(),
1287 Some(Datum::string("Testy McTest"))
1288 );
1289 assert_eq!(
1290 schema
1291 .accessor_by_field_id(17)
1292 .unwrap()
1293 .get(&test_struct)
1294 .unwrap(),
1295 Some(Datum::int(33))
1296 );
1297 }
1298
1299 #[test]
1300 fn test_highest_field_id() {
1301 let schema = table_schema_nested();
1302 assert_eq!(17, schema.highest_field_id());
1303
1304 let schema = table_schema_simple().0;
1305 assert_eq!(3, schema.highest_field_id());
1306 }
1307
1308 #[test]
1309 fn test_highest_field_id_no_fields() {
1310 let schema = Schema::builder().with_schema_id(1).build().unwrap();
1311 assert_eq!(0, schema.highest_field_id());
1312 }
1313
1314 #[test]
1315 fn test_field_ids_must_be_unique() {
1316 let reassigned_schema = Schema::builder()
1317 .with_schema_id(1)
1318 .with_identifier_field_ids(vec![5])
1319 .with_alias(BiHashMap::from_iter(vec![("bar_alias".to_string(), 3)]))
1320 .with_fields(vec![
1321 NestedField::required(5, "foo", Primitive(PrimitiveType::String)).into(),
1322 NestedField::optional(3, "bar", Primitive(PrimitiveType::Int)).into(),
1323 NestedField::optional(3, "baz", Primitive(PrimitiveType::Boolean)).into(),
1324 ])
1325 .build()
1326 .unwrap_err();
1327
1328 assert!(reassigned_schema.message().contains("'field.id' 3"));
1329 }
1330
1331 #[test]
1332 fn test_reassign_ids_empty_schema() {
1333 let schema = Schema::builder().with_schema_id(1).build().unwrap();
1334 let reassigned_schema = schema
1335 .clone()
1336 .into_builder()
1337 .with_reassigned_field_ids(0)
1338 .build()
1339 .unwrap();
1340
1341 assert_eq!(schema, reassigned_schema);
1342 assert_eq!(schema.highest_field_id(), 0);
1343 }
1344
1345 #[test]
1346 fn test_identifier_field_ids() {
1347 assert!(
1349 Schema::builder()
1350 .with_schema_id(1)
1351 .with_identifier_field_ids(vec![2])
1352 .with_fields(vec![
1353 NestedField::required(
1354 1,
1355 "Map",
1356 Map(MapType::new(
1357 NestedField::map_key_element(2, Primitive(PrimitiveType::String))
1358 .into(),
1359 NestedField::map_value_element(
1360 3,
1361 Primitive(PrimitiveType::Boolean),
1362 true,
1363 )
1364 .into(),
1365 )),
1366 )
1367 .into()
1368 ])
1369 .build()
1370 .is_err()
1371 );
1372 assert!(
1373 Schema::builder()
1374 .with_schema_id(1)
1375 .with_identifier_field_ids(vec![3])
1376 .with_fields(vec![
1377 NestedField::required(
1378 1,
1379 "Map",
1380 Map(MapType::new(
1381 NestedField::map_key_element(2, Primitive(PrimitiveType::String))
1382 .into(),
1383 NestedField::map_value_element(
1384 3,
1385 Primitive(PrimitiveType::Boolean),
1386 true,
1387 )
1388 .into(),
1389 )),
1390 )
1391 .into()
1392 ])
1393 .build()
1394 .is_err()
1395 );
1396
1397 assert!(
1399 Schema::builder()
1400 .with_schema_id(1)
1401 .with_identifier_field_ids(vec![2])
1402 .with_fields(vec![
1403 NestedField::required(
1404 1,
1405 "List",
1406 List(ListType::new(
1407 NestedField::list_element(2, Primitive(PrimitiveType::String), true)
1408 .into(),
1409 )),
1410 )
1411 .into()
1412 ])
1413 .build()
1414 .is_err()
1415 );
1416
1417 assert!(
1419 Schema::builder()
1420 .with_schema_id(1)
1421 .with_identifier_field_ids(vec![2])
1422 .with_fields(vec![
1423 NestedField::optional(
1424 1,
1425 "Struct",
1426 Struct(StructType::new(vec![
1427 NestedField::required(2, "name", Primitive(PrimitiveType::String))
1428 .into(),
1429 NestedField::optional(3, "age", Primitive(PrimitiveType::Int)).into(),
1430 ])),
1431 )
1432 .into()
1433 ])
1434 .build()
1435 .is_err()
1436 );
1437
1438 assert!(
1440 Schema::builder()
1441 .with_schema_id(1)
1442 .with_identifier_field_ids(vec![1])
1443 .with_fields(vec![
1444 NestedField::required(1, "Float", Primitive(PrimitiveType::Float),).into()
1445 ])
1446 .build()
1447 .is_err()
1448 );
1449 assert!(
1450 Schema::builder()
1451 .with_schema_id(1)
1452 .with_identifier_field_ids(vec![1])
1453 .with_fields(vec![
1454 NestedField::required(1, "Double", Primitive(PrimitiveType::Double),).into()
1455 ])
1456 .build()
1457 .is_err()
1458 );
1459
1460 assert!(
1462 Schema::builder()
1463 .with_schema_id(1)
1464 .with_identifier_field_ids(vec![1])
1465 .with_fields(vec![
1466 NestedField::required(1, "Required", Primitive(PrimitiveType::String),).into()
1467 ])
1468 .build()
1469 .is_ok()
1470 );
1471 assert!(
1472 Schema::builder()
1473 .with_schema_id(1)
1474 .with_identifier_field_ids(vec![1])
1475 .with_fields(vec![
1476 NestedField::optional(1, "Optional", Primitive(PrimitiveType::String),).into()
1477 ])
1478 .build()
1479 .is_err()
1480 );
1481 }
1482
1483 #[test]
1484 fn test_unknown_type_deserialization_rejects_non_null_default() {
1485 let field_json = serde_json::json!({
1486 "id": 1,
1487 "name": "empty",
1488 "required": false,
1489 "type": "unknown",
1490 "initial-default": 1
1491 });
1492
1493 let error = serde_json::from_value::<NestedField>(field_json.clone()).unwrap_err();
1494 assert!(
1495 error
1496 .to_string()
1497 .contains("Unknown type only supports null default values"),
1498 "unexpected error: {error}"
1499 );
1500
1501 let schema_json = serde_json::json!({
1502 "type": "struct",
1503 "schema-id": 1,
1504 "fields": [field_json]
1505 });
1506 assert!(serde_json::from_value::<Schema>(schema_json).is_err());
1507 }
1508
1509 #[test]
1510 fn test_unknown_type_deserialization_accepts_null_defaults() {
1511 let schema_json = serde_json::json!({
1512 "type": "struct",
1513 "schema-id": 1,
1514 "fields": [
1515 {
1516 "id": 1,
1517 "name": "empty",
1518 "required": false,
1519 "type": "unknown",
1520 "initial-default": null,
1521 "write-default": null
1522 }
1523 ]
1524 });
1525
1526 serde_json::from_value::<Schema>(schema_json).unwrap();
1527 }
1528
1529 #[test]
1530 fn test_unknown_type_accepts_null_container_defaults() {
1531 let cases = [
1532 (
1533 "struct",
1534 Struct(StructType::new(vec![
1535 NestedField::optional(2, "empty", Primitive(PrimitiveType::Unknown)).into(),
1536 ])),
1537 Literal::Struct(crate::spec::Struct::from_iter([None])),
1538 ),
1539 (
1540 "list",
1541 List(ListType::new(
1542 NestedField::list_element(2, Primitive(PrimitiveType::Unknown), false).into(),
1543 )),
1544 Literal::List(vec![None]),
1545 ),
1546 (
1547 "map",
1548 Map(MapType::optional(
1549 2,
1550 Primitive(PrimitiveType::String),
1551 3,
1552 Primitive(PrimitiveType::Unknown),
1553 )),
1554 Literal::Map(MapValue::from([(Literal::string("key"), None)])),
1555 ),
1556 ];
1557
1558 for (name, field_type, default) in cases {
1559 let schema = Schema::builder()
1560 .with_schema_id(1)
1561 .with_fields(vec![
1562 NestedField::optional(1, name, field_type)
1563 .with_write_default(default)
1564 .into(),
1565 ])
1566 .build()
1567 .unwrap();
1568 serde_json::to_value(schema).unwrap();
1569 }
1570 }
1571
1572 #[test]
1573 fn test_unknown_type_deserialization_rejects_non_null_container_defaults() {
1574 let cases = [
1575 (
1576 "struct",
1577 serde_json::json!({
1578 "type": "struct",
1579 "fields": [{
1580 "id": 2,
1581 "name": "empty",
1582 "required": false,
1583 "type": "unknown"
1584 }]
1585 }),
1586 serde_json::json!({"2": 1}),
1587 ),
1588 (
1589 "list",
1590 serde_json::json!({
1591 "type": "list",
1592 "element-id": 2,
1593 "element-required": false,
1594 "element": "unknown"
1595 }),
1596 serde_json::json!([1]),
1597 ),
1598 (
1599 "map",
1600 serde_json::json!({
1601 "type": "map",
1602 "key-id": 2,
1603 "key": "string",
1604 "value-id": 3,
1605 "value-required": false,
1606 "value": "unknown"
1607 }),
1608 serde_json::json!({"keys": ["key"], "values": [1]}),
1609 ),
1610 ];
1611
1612 for (name, field_type, default) in cases {
1613 let schema_json = serde_json::json!({
1614 "type": "struct",
1615 "schema-id": 1,
1616 "fields": [{
1617 "id": 1,
1618 "name": name,
1619 "required": false,
1620 "type": field_type,
1621 "initial-default": default
1622 }]
1623 });
1624
1625 assert!(
1626 serde_json::from_value::<Schema>(schema_json).is_err(),
1627 "non-null unknown default in {name} should be rejected"
1628 );
1629 }
1630 }
1631}