1use crate::error::ArrowError;
6use std::cmp::Ordering;
7use std::hash::{Hash, Hasher};
8use std::sync::Arc;
9
10use crate::datatype::DataType;
11#[cfg(feature = "canonical_extension_types")]
12use crate::extension::CanonicalExtensionType;
13use crate::schema::SchemaBuilder;
14use crate::{
15 Fields, Metadata, UnionFields, UnionMode,
16 extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY, ExtensionType},
17};
18
19pub type FieldRef = Arc<Field>;
21
22#[derive(Clone)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35pub struct Field {
36 name: String,
37 data_type: DataType,
38 nullable: bool,
39 #[deprecated(
40 since = "54.0.0",
41 note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
42 )]
43 dict_id: i64,
44 dict_is_ordered: bool,
45 metadata: Metadata,
47}
48
49impl std::fmt::Debug for Field {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 #![expect(deprecated)] let Self {
53 name,
54 data_type,
55 nullable,
56 dict_id,
57 dict_is_ordered,
58 metadata,
59 } = self;
60
61 let mut s = f.debug_struct("Field");
62
63 if name != "item" {
64 s.field("name", name);
66 }
67
68 s.field("data_type", data_type);
69
70 if *nullable {
71 s.field("nullable", nullable);
72 }
73
74 if *dict_id != 0 {
75 s.field("dict_id", dict_id);
76 }
77
78 if *dict_is_ordered {
79 s.field("dict_is_ordered", dict_is_ordered);
80 }
81
82 if !metadata.is_empty() {
83 s.field("metadata", metadata);
84 }
85 s.finish()
86 }
87}
88
89impl PartialEq for Field {
95 fn eq(&self, other: &Self) -> bool {
96 self.name == other.name
97 && self.data_type == other.data_type
98 && self.nullable == other.nullable
99 && self.metadata == other.metadata
100 }
101}
102
103impl Eq for Field {}
104
105impl PartialOrd for Field {
106 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
107 Some(self.cmp(other))
108 }
109}
110
111impl Ord for Field {
112 fn cmp(&self, other: &Self) -> Ordering {
113 self.name
114 .cmp(other.name())
115 .then_with(|| self.data_type.cmp(other.data_type()))
116 .then_with(|| self.nullable.cmp(&other.nullable))
117 .then_with(|| self.metadata.cmp(&other.metadata))
118 }
119}
120
121impl Hash for Field {
122 fn hash<H: Hasher>(&self, state: &mut H) {
123 self.name.hash(state);
124 self.data_type.hash(state);
125 self.nullable.hash(state);
126 self.metadata.hash(state);
128 }
129}
130
131impl AsRef<Field> for Field {
132 fn as_ref(&self) -> &Field {
133 self
134 }
135}
136
137impl Field {
138 pub const LIST_FIELD_DEFAULT_NAME: &'static str = "item";
140 pub const MAP_ENTRIES_FIELD_DEFAULT_NAME: &'static str = "entries";
144 pub const MAP_KEY_FIELD_DEFAULT_NAME: &'static str = "key";
148 pub const MAP_VALUE_FIELD_DEFAULT_NAME: &'static str = "value";
152 pub const REE_RUN_ENDS_FIELD_DEFAULT_NAME: &'static str = "run_ends";
156 pub const REE_VALUES_FIELD_DEFAULT_NAME: &'static str = "values";
160
161 pub fn new(name: impl Into<String>, data_type: DataType, nullable: bool) -> Self {
169 #[expect(deprecated)]
170 Field {
171 name: name.into(),
172 data_type,
173 nullable,
174 dict_id: 0,
175 dict_is_ordered: false,
176 metadata: Default::default(),
177 }
178 }
179
180 pub fn new_list_field(data_type: DataType, nullable: bool) -> Self {
195 Self::new(Self::LIST_FIELD_DEFAULT_NAME, data_type, nullable)
196 }
197
198 #[deprecated(
200 since = "54.0.0",
201 note = "The ability to preserve dictionary IDs will be removed. With the dict_id field disappearing this function signature will change by removing the dict_id parameter."
202 )]
203 pub fn new_dict(
204 name: impl Into<String>,
205 data_type: DataType,
206 nullable: bool,
207 dict_id: i64,
208 dict_is_ordered: bool,
209 ) -> Self {
210 #[expect(deprecated)]
211 Field {
212 name: name.into(),
213 data_type,
214 nullable,
215 dict_id,
216 dict_is_ordered,
217 metadata: Default::default(),
218 }
219 }
220
221 pub fn new_dictionary(
229 name: impl Into<String>,
230 key: DataType,
231 value: DataType,
232 nullable: bool,
233 ) -> Self {
234 assert!(
235 key.is_dictionary_key_type(),
236 "{key} is not a valid dictionary key"
237 );
238 let data_type = DataType::Dictionary(Box::new(key), Box::new(value));
239 Self::new(name, data_type, nullable)
240 }
241
242 pub fn new_struct(name: impl Into<String>, fields: impl Into<Fields>, nullable: bool) -> Self {
248 Self::new(name, DataType::Struct(fields.into()), nullable)
249 }
250
251 pub fn new_list(name: impl Into<String>, value: impl Into<FieldRef>, nullable: bool) -> Self {
257 Self::new(name, DataType::List(value.into()), nullable)
258 }
259
260 pub fn new_large_list(
266 name: impl Into<String>,
267 value: impl Into<FieldRef>,
268 nullable: bool,
269 ) -> Self {
270 Self::new(name, DataType::LargeList(value.into()), nullable)
271 }
272
273 pub fn new_fixed_size_list(
280 name: impl Into<String>,
281 value: impl Into<FieldRef>,
282 size: i32,
283 nullable: bool,
284 ) -> Self {
285 Self::new(name, DataType::FixedSizeList(value.into(), size), nullable)
286 }
287
288 pub fn new_map(
297 name: impl Into<String>,
298 entries: impl Into<String>,
299 keys: impl Into<FieldRef>,
300 values: impl Into<FieldRef>,
301 sorted: bool,
302 nullable: bool,
303 ) -> Self {
304 let data_type = DataType::Map(
305 Arc::new(Field::new(
306 entries.into(),
307 DataType::Struct(Fields::from([keys.into(), values.into()])),
308 false, )),
310 sorted,
311 );
312 Self::new(name, data_type, nullable)
313 }
314
315 pub fn new_union<S, F, T>(name: S, type_ids: T, fields: F, mode: UnionMode) -> Self
329 where
330 S: Into<String>,
331 F: IntoIterator,
332 F::Item: Into<FieldRef>,
333 T: IntoIterator<Item = i8>,
334 {
335 Self::new(
336 name,
337 DataType::Union(
338 UnionFields::try_new(type_ids, fields).expect("Invalid UnionField"),
339 mode,
340 ),
341 false, )
343 }
344
345 #[inline]
347 pub fn set_metadata(&mut self, metadata: impl Into<Metadata>) {
348 self.metadata = metadata.into();
349 }
350
351 pub fn with_metadata(mut self, metadata: impl Into<Metadata>) -> Self {
353 self.set_metadata(metadata);
354 self
355 }
356
357 #[inline]
359 pub const fn metadata(&self) -> &Metadata {
360 &self.metadata
361 }
362
363 #[inline]
365 pub fn metadata_mut(&mut self) -> &mut Metadata {
366 &mut self.metadata
367 }
368
369 #[inline]
371 pub const fn name(&self) -> &String {
372 &self.name
373 }
374
375 #[inline]
377 pub fn set_name(&mut self, name: impl Into<String>) {
378 self.name = name.into();
379 }
380
381 pub fn with_name(mut self, name: impl Into<String>) -> Self {
391 self.set_name(name);
392 self
393 }
394
395 #[inline]
397 pub const fn data_type(&self) -> &DataType {
398 &self.data_type
399 }
400
401 #[inline]
411 pub fn set_data_type(&mut self, data_type: DataType) {
412 self.data_type = data_type;
413 }
414
415 pub fn with_data_type(mut self, data_type: DataType) -> Self {
425 self.set_data_type(data_type);
426 self
427 }
428
429 pub fn extension_type_name(&self) -> Option<&str> {
448 self.metadata()
449 .get(EXTENSION_TYPE_NAME_KEY)
450 .map(String::as_ref)
451 }
452
453 pub fn extension_type_metadata(&self) -> Option<&str> {
472 self.metadata()
473 .get(EXTENSION_TYPE_METADATA_KEY)
474 .map(String::as_ref)
475 }
476
477 #[inline]
486 pub fn has_valid_extension_type<E: ExtensionType>(&self) -> bool {
487 if self.extension_type_name() != Some(E::NAME) {
488 return false;
489 }
490
491 let ext_metadata = self
492 .metadata()
493 .get(EXTENSION_TYPE_METADATA_KEY)
494 .map(|s| s.as_str());
495
496 E::deserialize_metadata(ext_metadata)
497 .and_then(|metadata| E::validate(self.data_type(), metadata))
498 .is_ok()
499 }
500
501 pub fn try_extension_type<E: ExtensionType>(&self) -> Result<E, ArrowError> {
546 E::try_new_from_field_metadata(self.data_type(), self.metadata())
547 }
548
549 pub fn extension_type<E: ExtensionType>(&self) -> E {
557 self.try_extension_type::<E>()
558 .unwrap_or_else(|e| panic!("{e}"))
559 }
560
561 pub fn try_with_extension_type<E: ExtensionType>(
574 &mut self,
575 extension_type: E,
576 ) -> Result<(), ArrowError> {
577 extension_type.supports_data_type(&self.data_type)?;
579
580 self.metadata
581 .insert(EXTENSION_TYPE_NAME_KEY.to_owned(), E::NAME.to_owned());
582 match extension_type.serialize_metadata() {
583 Some(metadata) => self
584 .metadata
585 .insert(EXTENSION_TYPE_METADATA_KEY.to_owned(), metadata),
586 None => self.metadata.remove(EXTENSION_TYPE_METADATA_KEY),
589 };
590
591 Ok(())
592 }
593
594 pub fn with_extension_type<E: ExtensionType>(mut self, extension_type: E) -> Self {
602 self.try_with_extension_type(extension_type)
603 .unwrap_or_else(|e| panic!("{e}"));
604 self
605 }
606
607 #[cfg(feature = "canonical_extension_types")]
616 pub fn try_canonical_extension_type(&self) -> Result<CanonicalExtensionType, ArrowError> {
617 CanonicalExtensionType::try_from(self)
618 }
619
620 #[inline]
624 pub const fn is_nullable(&self) -> bool {
625 self.nullable
626 }
627
628 #[inline]
638 pub fn set_nullable(&mut self, nullable: bool) {
639 self.nullable = nullable;
640 }
641
642 pub fn with_nullable(mut self, nullable: bool) -> Self {
652 self.set_nullable(nullable);
653 self
654 }
655
656 pub(crate) fn fields(&self) -> Vec<&Field> {
659 let mut collected_fields = vec![self];
660 collected_fields.append(&mut Field::_fields(&self.data_type));
661
662 collected_fields
663 }
664
665 fn _fields(dt: &DataType) -> Vec<&Field> {
666 match dt {
667 DataType::Struct(fields) => fields.iter().flat_map(|f| f.fields()).collect(),
668 DataType::Union(fields, _) => fields.iter().flat_map(|(_, f)| f.fields()).collect(),
669 DataType::List(field)
670 | DataType::LargeList(field)
671 | DataType::ListView(field)
672 | DataType::LargeListView(field)
673 | DataType::FixedSizeList(field, _)
674 | DataType::Map(field, _) => field.fields(),
675 DataType::Dictionary(_, value_field) => Field::_fields(value_field.as_ref()),
676 DataType::RunEndEncoded(_, field) => field.fields(),
677 _ => vec![],
678 }
679 }
680
681 #[inline]
684 #[deprecated(
685 since = "54.0.0",
686 note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
687 )]
688 pub(crate) fn fields_with_dict_id(&self, id: i64) -> Vec<&Field> {
689 self.fields()
690 .into_iter()
691 .filter(|&field| {
692 #[expect(deprecated)]
693 let matching_dict_id = field.dict_id == id;
694 matches!(field.data_type(), DataType::Dictionary(_, _)) && matching_dict_id
695 })
696 .collect()
697 }
698
699 #[inline]
701 #[deprecated(
702 since = "54.0.0",
703 note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
704 )]
705 pub const fn dict_id(&self) -> Option<i64> {
706 match self.data_type {
707 #[expect(deprecated)]
708 DataType::Dictionary(_, _) => Some(self.dict_id),
709 _ => None,
710 }
711 }
712
713 #[inline]
728 pub const fn dict_is_ordered(&self) -> Option<bool> {
729 match self.data_type {
730 DataType::Dictionary(_, _) => Some(self.dict_is_ordered),
731 _ => None,
732 }
733 }
734
735 pub fn with_dict_is_ordered(mut self, dict_is_ordered: bool) -> Self {
741 if matches!(self.data_type, DataType::Dictionary(_, _)) {
742 self.dict_is_ordered = dict_is_ordered;
743 }
744 self
745 }
746
747 pub fn try_merge(&mut self, from: &Field) -> Result<(), ArrowError> {
762 if from.dict_is_ordered != self.dict_is_ordered {
763 return Err(ArrowError::SchemaError(format!(
764 "Fail to merge schema field '{}' because from dict_is_ordered = {} does not match {}",
765 self.name, from.dict_is_ordered, self.dict_is_ordered
766 )));
767 }
768 match (self.metadata().is_empty(), from.metadata().is_empty()) {
770 (false, false) => {
771 let mut merged = self.metadata().clone();
772 for (key, from_value) in from.metadata() {
773 if let Some(self_value) = self.metadata.get(key) {
774 if self_value != from_value {
775 return Err(ArrowError::SchemaError(format!(
776 "Fail to merge field '{}' due to conflicting metadata data value for key {}.
777 From value = {} does not match {}", self.name, key, from_value, self_value),
778 ));
779 }
780 } else {
781 merged.insert(key.clone(), from_value.clone());
782 }
783 }
784 self.set_metadata(merged);
785 }
786 (true, false) => {
787 self.set_metadata(from.metadata().clone());
788 }
789 _ => {}
790 }
791 match &mut self.data_type {
792 DataType::Struct(nested_fields) => match &from.data_type {
793 DataType::Struct(from_nested_fields) => {
794 let mut builder = SchemaBuilder::new();
795 nested_fields
796 .iter()
797 .chain(from_nested_fields)
798 .try_for_each(|f| builder.try_merge(f))?;
799 *nested_fields = builder.finish().fields;
800 }
801 DataType::Null => {
802 self.nullable = true;
803 }
804 _ => {
805 return Err(ArrowError::SchemaError(format!(
806 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::Struct",
807 self.name, from.data_type
808 )));
809 }
810 },
811 DataType::Union(nested_fields, mode) => match &from.data_type {
812 DataType::Union(from_nested_fields, from_mode) => {
813 if mode != from_mode {
814 return Err(ArrowError::SchemaError(format!(
815 "Fail to merge schema field '{}' because the from union mode = {:?} does not match {:?}",
816 self.name, from_mode, mode
817 )));
818 }
819 nested_fields.try_merge(from_nested_fields)?;
820 }
821 DataType::Null => {
822 self.nullable = true;
823 }
824 _ => {
825 return Err(ArrowError::SchemaError(format!(
826 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::Union",
827 self.name, from.data_type
828 )));
829 }
830 },
831 DataType::List(field) => match &from.data_type {
832 DataType::List(from_field) => {
833 let mut f = (**field).clone();
834 f.try_merge(from_field)?;
835 (*field) = Arc::new(f);
836 }
837 DataType::Null => {
838 self.nullable = true;
839 }
840 _ => {
841 return Err(ArrowError::SchemaError(format!(
842 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::List",
843 self.name, from.data_type
844 )));
845 }
846 },
847 DataType::LargeList(field) => match &from.data_type {
848 DataType::LargeList(from_field) => {
849 let mut f = (**field).clone();
850 f.try_merge(from_field)?;
851 (*field) = Arc::new(f);
852 }
853 DataType::Null => {
854 self.nullable = true;
855 }
856 _ => {
857 return Err(ArrowError::SchemaError(format!(
858 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::LargeList",
859 self.name, from.data_type
860 )));
861 }
862 },
863 DataType::Null => {
864 self.nullable = true;
865 self.data_type = from.data_type.clone();
866 }
867 DataType::Boolean
868 | DataType::Int8
869 | DataType::Int16
870 | DataType::Int32
871 | DataType::Int64
872 | DataType::UInt8
873 | DataType::UInt16
874 | DataType::UInt32
875 | DataType::UInt64
876 | DataType::Float16
877 | DataType::Float32
878 | DataType::Float64
879 | DataType::Timestamp(_, _)
880 | DataType::Date32
881 | DataType::Date64
882 | DataType::Time32(_)
883 | DataType::Time64(_)
884 | DataType::Duration(_)
885 | DataType::Binary
886 | DataType::LargeBinary
887 | DataType::BinaryView
888 | DataType::Interval(_)
889 | DataType::LargeListView(_)
890 | DataType::ListView(_)
891 | DataType::Map(_, _)
892 | DataType::Dictionary(_, _)
893 | DataType::RunEndEncoded(_, _)
894 | DataType::FixedSizeList(_, _)
895 | DataType::FixedSizeBinary(_)
896 | DataType::Utf8
897 | DataType::LargeUtf8
898 | DataType::Utf8View
899 | DataType::Decimal32(_, _)
900 | DataType::Decimal64(_, _)
901 | DataType::Decimal128(_, _)
902 | DataType::Decimal256(_, _) => {
903 if from.data_type == DataType::Null {
904 self.nullable = true;
905 } else if self.data_type != from.data_type {
906 return Err(ArrowError::SchemaError(format!(
907 "Fail to merge schema field '{}' because the from data_type = {} does not equal {}",
908 self.name, from.data_type, self.data_type
909 )));
910 }
911 }
912 }
913 self.nullable |= from.nullable;
914
915 Ok(())
916 }
917
918 pub fn contains(&self, other: &Field) -> bool {
924 self.name == other.name
925 && self.data_type.contains(&other.data_type)
926 && self.dict_is_ordered == other.dict_is_ordered
927 && (self.nullable || !other.nullable)
929 && other.metadata.iter().all(|(k, v1)| {
931 self.metadata.get(k).is_some_and(|v2| v1 == v2)
932 })
933 }
934
935 pub fn size(&self) -> usize {
939 std::mem::size_of_val(self) - std::mem::size_of_val(&self.data_type)
940 + self.data_type.size()
941 + self.name.capacity()
942 + (std::mem::size_of::<(String, String)>() * self.metadata.len())
943 + self
944 .metadata
945 .iter()
946 .map(|(k, v)| k.capacity() + v.capacity())
947 .sum::<usize>()
948 }
949}
950
951impl std::fmt::Display for Field {
952 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
953 #![expect(deprecated)] let Self {
955 name,
956 data_type,
957 nullable,
958 dict_id,
959 dict_is_ordered,
960 metadata,
961 } = self;
962 let maybe_nullable = if *nullable { "nullable " } else { "" };
963 let metadata_str = if metadata.is_empty() {
964 String::new()
965 } else {
966 format!(", metadata: {metadata:?}")
967 };
968 let dict_id_str = if dict_id == &0 {
969 String::new()
970 } else {
971 format!(", dict_id: {dict_id}")
972 };
973 let dict_is_ordered_str = if *dict_is_ordered {
974 ", dict_is_ordered"
975 } else {
976 ""
977 };
978 write!(
979 f,
980 "Field {{ {name:?}: {maybe_nullable}{data_type}{dict_id_str}{dict_is_ordered_str}{metadata_str} }}"
981 )
982 }
983}
984
985#[cfg(test)]
986mod test {
987 use super::*;
988 use std::collections::HashMap;
989 use std::collections::hash_map::DefaultHasher;
990
991 #[derive(Debug, Clone, Copy)]
992 struct TestExtensionType;
993
994 impl ExtensionType for TestExtensionType {
995 const NAME: &'static str = "test.extension";
996 type Metadata = ();
997
998 fn metadata(&self) -> &Self::Metadata {
999 &()
1000 }
1001
1002 fn serialize_metadata(&self) -> Option<String> {
1003 None
1004 }
1005
1006 fn deserialize_metadata(metadata: Option<&str>) -> Result<Self::Metadata, ArrowError> {
1007 metadata.map_or(Ok(()), |_| {
1008 Err(ArrowError::InvalidArgumentError(
1009 "TestExtensionType expects no metadata".to_owned(),
1010 ))
1011 })
1012 }
1013
1014 fn supports_data_type(&self, _data_type: &DataType) -> Result<(), ArrowError> {
1015 Ok(())
1016 }
1017
1018 fn try_new(_data_type: &DataType, _metadata: Self::Metadata) -> Result<Self, ArrowError> {
1019 Ok(Self)
1020 }
1021 }
1022
1023 #[test]
1024 fn test_has_valid_extension_type() {
1025 let no_extension = Field::new("f", DataType::Null, false);
1026 assert!(!no_extension.has_valid_extension_type::<TestExtensionType>());
1027
1028 let matching_name = Field::new("f", DataType::Null, false)
1029 .with_metadata([(EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME)]);
1030 assert!(matching_name.has_valid_extension_type::<TestExtensionType>());
1031
1032 let matching_name_with_invalid_metadata = Field::new("f", DataType::Null, false)
1033 .with_metadata([
1034 (EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME),
1035 (EXTENSION_TYPE_METADATA_KEY, "invalid"),
1036 ]);
1037 assert!(
1038 !matching_name_with_invalid_metadata.has_valid_extension_type::<TestExtensionType>()
1039 );
1040
1041 let different_name = Field::new("f", DataType::Null, false)
1042 .with_metadata([(EXTENSION_TYPE_NAME_KEY, "some.other_extension")]);
1043 assert!(!different_name.has_valid_extension_type::<TestExtensionType>());
1044 }
1045
1046 #[test]
1047 fn test_new_with_string() {
1048 let s = "c1";
1050 Field::new(s, DataType::Int64, false);
1051 }
1052
1053 #[test]
1054 fn test_new_dict_with_string() {
1055 let s = "c1";
1057 #[expect(deprecated)]
1058 Field::new_dict(s, DataType::Int64, false, 4, false);
1059 }
1060
1061 #[test]
1062 #[cfg_attr(miri, ignore)] fn test_debug_format_field() {
1064 insta::assert_debug_snapshot!(Field::new("item", DataType::UInt8, false), @r"
1066 Field {
1067 data_type: UInt8,
1068 }
1069 ");
1070 insta::assert_debug_snapshot!(Field::new("column", DataType::LargeUtf8, true), @r#"
1071 Field {
1072 name: "column",
1073 data_type: LargeUtf8,
1074 nullable: true,
1075 }
1076 "#);
1077 }
1078
1079 #[test]
1080 fn test_merge_union_modes() {
1081 for mode in [UnionMode::Sparse, UnionMode::Dense] {
1082 let mut field = Field::new_union(
1083 "u",
1084 [0],
1085 [Field::new("number", DataType::Int32, false)],
1086 mode,
1087 );
1088 let other_mode = match mode {
1089 UnionMode::Sparse => UnionMode::Dense,
1090 UnionMode::Dense => UnionMode::Sparse,
1091 };
1092 let incompatible = Field::new_union(
1093 "u",
1094 [0],
1095 [Field::new("number", DataType::Int32, false)],
1096 other_mode,
1097 );
1098 assert!(matches!(
1099 field.try_merge(&incompatible),
1100 Err(ArrowError::SchemaError(_))
1101 ));
1102
1103 let compatible =
1104 Field::new_union("u", [1], [Field::new("text", DataType::Utf8, false)], mode);
1105 field.try_merge(&compatible).unwrap();
1106 let expected = Field::new_union(
1107 "u",
1108 [0, 1],
1109 [
1110 Field::new("number", DataType::Int32, false),
1111 Field::new("text", DataType::Utf8, false),
1112 ],
1113 mode,
1114 );
1115 assert_eq!(field, expected);
1116 }
1117 }
1118
1119 #[test]
1120 fn test_merge_incompatible_types() {
1121 let mut field = Field::new("c1", DataType::Int64, false);
1122 let result = field
1123 .try_merge(&Field::new("c1", DataType::Float32, true))
1124 .expect_err("should fail")
1125 .to_string();
1126 assert_eq!(
1127 "Schema error: Fail to merge schema field 'c1' because the from data_type = Float32 does not equal Int64",
1128 result
1129 );
1130 }
1131
1132 #[test]
1133 fn test_merge_with_null() {
1134 let mut field1 = Field::new("c1", DataType::Null, true);
1135 field1
1136 .try_merge(&Field::new("c1", DataType::Float32, false))
1137 .expect("should widen type to nullable float");
1138 assert_eq!(Field::new("c1", DataType::Float32, true), field1);
1139
1140 let mut field2 = Field::new("c2", DataType::Utf8, false);
1141 field2
1142 .try_merge(&Field::new("c2", DataType::Null, true))
1143 .expect("should widen type to nullable utf8");
1144 assert_eq!(Field::new("c2", DataType::Utf8, true), field2);
1145 }
1146
1147 #[test]
1148 fn test_merge_with_nested_null() {
1149 let mut struct1 = Field::new(
1150 "s1",
1151 DataType::Struct(Fields::from(vec![Field::new(
1152 "inner",
1153 DataType::Float32,
1154 false,
1155 )])),
1156 false,
1157 );
1158
1159 let struct2 = Field::new(
1160 "s2",
1161 DataType::Struct(Fields::from(vec![Field::new(
1162 "inner",
1163 DataType::Null,
1164 false,
1165 )])),
1166 true,
1167 );
1168
1169 struct1
1170 .try_merge(&struct2)
1171 .expect("should widen inner field's type to nullable float");
1172 assert_eq!(
1173 Field::new(
1174 "s1",
1175 DataType::Struct(Fields::from(vec![Field::new(
1176 "inner",
1177 DataType::Float32,
1178 true,
1179 )])),
1180 true,
1181 ),
1182 struct1
1183 );
1184
1185 let mut list1 = Field::new(
1186 "l1",
1187 DataType::List(Field::new("inner", DataType::Float32, false).into()),
1188 false,
1189 );
1190
1191 let list2 = Field::new(
1192 "l2",
1193 DataType::List(Field::new("inner", DataType::Null, false).into()),
1194 true,
1195 );
1196
1197 list1
1198 .try_merge(&list2)
1199 .expect("should widen inner field's type to nullable float");
1200 assert_eq!(
1201 Field::new(
1202 "l1",
1203 DataType::List(Field::new("inner", DataType::Float32, true).into()),
1204 true,
1205 ),
1206 list1
1207 );
1208
1209 let mut large_list1 = Field::new(
1210 "ll1",
1211 DataType::LargeList(Field::new("inner", DataType::Float32, false).into()),
1212 false,
1213 );
1214
1215 let large_list2 = Field::new(
1216 "ll2",
1217 DataType::LargeList(Field::new("inner", DataType::Null, false).into()),
1218 true,
1219 );
1220
1221 large_list1
1222 .try_merge(&large_list2)
1223 .expect("should widen inner field's type to nullable float");
1224 assert_eq!(
1225 Field::new(
1226 "ll1",
1227 DataType::LargeList(Field::new("inner", DataType::Float32, true).into()),
1228 true,
1229 ),
1230 large_list1
1231 );
1232 }
1233
1234 #[test]
1235 fn test_fields_with_dict_id() {
1236 #[expect(deprecated)]
1237 let dict1 = Field::new_dict(
1238 "dict1",
1239 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1240 false,
1241 10,
1242 false,
1243 );
1244 #[expect(deprecated)]
1245 let dict2 = Field::new_dict(
1246 "dict2",
1247 DataType::Dictionary(DataType::Int32.into(), DataType::Int8.into()),
1248 false,
1249 20,
1250 false,
1251 );
1252
1253 let field = Field::new(
1254 "struct<dict1, list[struct<dict2, list[struct<dict1]>]>",
1255 DataType::Struct(Fields::from(vec![
1256 dict1.clone(),
1257 Field::new(
1258 "list[struct<dict1, list[struct<dict2>]>]",
1259 DataType::List(Arc::new(Field::new(
1260 "struct<dict1, list[struct<dict2>]>",
1261 DataType::Struct(Fields::from(vec![
1262 dict1.clone(),
1263 Field::new(
1264 "list[struct<dict2>]",
1265 DataType::List(Arc::new(Field::new(
1266 "struct<dict2>",
1267 DataType::Struct(vec![dict2.clone()].into()),
1268 false,
1269 ))),
1270 false,
1271 ),
1272 ])),
1273 false,
1274 ))),
1275 false,
1276 ),
1277 ])),
1278 false,
1279 );
1280
1281 #[expect(deprecated)]
1282 for field in field.fields_with_dict_id(10) {
1283 assert_eq!(dict1, *field);
1284 }
1285 #[expect(deprecated)]
1286 for field in field.fields_with_dict_id(20) {
1287 assert_eq!(dict2, *field);
1288 }
1289 }
1290
1291 fn get_field_hash(field: &Field) -> u64 {
1292 let mut s = DefaultHasher::new();
1293 field.hash(&mut s);
1294 s.finish()
1295 }
1296
1297 #[test]
1298 fn test_field_comparison_case() {
1299 #[expect(deprecated)]
1301 let dict1 = Field::new_dict(
1302 "dict1",
1303 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1304 false,
1305 10,
1306 false,
1307 );
1308 #[expect(deprecated)]
1309 let dict2 = Field::new_dict(
1310 "dict1",
1311 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1312 false,
1313 20,
1314 false,
1315 );
1316
1317 assert_eq!(dict1, dict2);
1318 assert_eq!(get_field_hash(&dict1), get_field_hash(&dict2));
1319
1320 #[expect(deprecated)]
1321 let dict1 = Field::new_dict(
1322 "dict0",
1323 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1324 false,
1325 10,
1326 false,
1327 );
1328
1329 assert_ne!(dict1, dict2);
1330 assert_ne!(get_field_hash(&dict1), get_field_hash(&dict2));
1331 }
1332
1333 #[test]
1334 fn test_field_comparison_metadata() {
1335 let f1 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1336 (String::from("k1"), String::from("v1")),
1337 (String::from("k2"), String::from("v2")),
1338 ]));
1339 let f2 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1340 (String::from("k1"), String::from("v1")),
1341 (String::from("k3"), String::from("v3")),
1342 ]));
1343 let f3 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1344 (String::from("k1"), String::from("v1")),
1345 (String::from("k3"), String::from("v4")),
1346 ]));
1347
1348 assert!(f1.cmp(&f2).is_lt());
1349 assert!(f2.cmp(&f3).is_lt());
1350 assert!(f1.cmp(&f3).is_lt());
1351 }
1352
1353 #[test]
1354 #[expect(clippy::needless_borrows_for_generic_args)] fn test_field_as_ref() {
1356 let field = || Field::new("x", DataType::Binary, false);
1357
1358 fn accept_ref(_: impl AsRef<Field>) {}
1361
1362 accept_ref(field());
1363 accept_ref(&field());
1364 accept_ref(&&field());
1365 accept_ref(Arc::new(field()));
1366 accept_ref(&Arc::new(field()));
1367 accept_ref(&&Arc::new(field()));
1368
1369 fn accept_refs(_: impl IntoIterator<Item: AsRef<Field>>) {}
1372
1373 accept_refs(vec![field()]);
1374 accept_refs(vec![&field()]);
1375 accept_refs(vec![Arc::new(field())]);
1376 accept_refs(vec![&Arc::new(field())]);
1377 accept_refs(&vec![field()]);
1378 accept_refs(&vec![&field()]);
1379 accept_refs(&vec![Arc::new(field())]);
1380 accept_refs(&vec![&Arc::new(field())]);
1381 }
1382
1383 #[test]
1384 fn test_contains_reflexivity() {
1385 let mut field = Field::new("field1", DataType::Float16, false);
1386 field.set_metadata(HashMap::from([
1387 (String::from("k0"), String::from("v0")),
1388 (String::from("k1"), String::from("v1")),
1389 ]));
1390 assert!(field.contains(&field))
1391 }
1392
1393 #[test]
1394 fn test_contains_transitivity() {
1395 let child_field = Field::new("child1", DataType::Float16, false);
1396
1397 let mut field1 = Field::new(
1398 "field1",
1399 DataType::Struct(Fields::from(vec![child_field])),
1400 false,
1401 );
1402 field1.set_metadata(HashMap::from([(String::from("k1"), String::from("v1"))]));
1403
1404 let mut field2 = Field::new("field1", DataType::Struct(Fields::default()), true);
1405 field2.set_metadata(HashMap::from([(String::from("k2"), String::from("v2"))]));
1406 field2.try_merge(&field1).unwrap();
1407
1408 let mut field3 = Field::new("field1", DataType::Struct(Fields::default()), false);
1409 field3.set_metadata(HashMap::from([(String::from("k3"), String::from("v3"))]));
1410 field3.try_merge(&field2).unwrap();
1411
1412 assert!(field2.contains(&field1));
1413 assert!(field3.contains(&field2));
1414 assert!(field3.contains(&field1));
1415
1416 assert!(!field1.contains(&field2));
1417 assert!(!field1.contains(&field3));
1418 assert!(!field2.contains(&field3));
1419 }
1420
1421 #[test]
1422 fn test_contains_nullable() {
1423 let field1 = Field::new("field1", DataType::Boolean, true);
1424 let field2 = Field::new("field1", DataType::Boolean, false);
1425 assert!(field1.contains(&field2));
1426 assert!(!field2.contains(&field1));
1427 }
1428
1429 #[test]
1430 fn test_contains_must_have_same_fields() {
1431 let child_field1 = Field::new("child1", DataType::Float16, false);
1432 let child_field2 = Field::new("child2", DataType::Float16, false);
1433
1434 let field1 = Field::new(
1435 "field1",
1436 DataType::Struct(vec![child_field1.clone()].into()),
1437 true,
1438 );
1439 let field2 = Field::new(
1440 "field1",
1441 DataType::Struct(vec![child_field1, child_field2].into()),
1442 true,
1443 );
1444
1445 assert!(!field1.contains(&field2));
1446 assert!(!field2.contains(&field1));
1447
1448 let field1 = Field::new(
1450 "field1",
1451 DataType::Union(
1452 UnionFields::try_new(
1453 vec![1, 2],
1454 vec![
1455 Field::new("field1", DataType::UInt8, true),
1456 Field::new("field3", DataType::Utf8, false),
1457 ],
1458 )
1459 .unwrap(),
1460 UnionMode::Dense,
1461 ),
1462 true,
1463 );
1464 let field2 = Field::new(
1465 "field1",
1466 DataType::Union(
1467 UnionFields::try_new(
1468 vec![1, 3],
1469 vec![
1470 Field::new("field1", DataType::UInt8, false),
1471 Field::new("field3", DataType::Utf8, false),
1472 ],
1473 )
1474 .unwrap(),
1475 UnionMode::Dense,
1476 ),
1477 true,
1478 );
1479 assert!(!field1.contains(&field2));
1480
1481 let field1 = Field::new(
1483 "field1",
1484 DataType::Union(
1485 UnionFields::try_new(
1486 vec![1, 2],
1487 vec![
1488 Field::new("field1", DataType::UInt8, true),
1489 Field::new("field3", DataType::Utf8, false),
1490 ],
1491 )
1492 .unwrap(),
1493 UnionMode::Dense,
1494 ),
1495 true,
1496 );
1497 let field2 = Field::new(
1498 "field1",
1499 DataType::Union(
1500 UnionFields::try_new(
1501 vec![1, 2],
1502 vec![
1503 Field::new("field1", DataType::UInt8, false),
1504 Field::new("field3", DataType::Utf8, false),
1505 ],
1506 )
1507 .unwrap(),
1508 UnionMode::Dense,
1509 ),
1510 true,
1511 );
1512 assert!(field1.contains(&field2));
1513 }
1514
1515 #[cfg(feature = "serde")]
1516 fn assert_binary_serde_round_trip(field: Field) {
1517 let serialized = postcard::to_stdvec(&field).unwrap();
1518 let deserialized: Field = postcard::from_bytes(&serialized).unwrap();
1519 assert_eq!(field, deserialized)
1520 }
1521
1522 #[cfg(feature = "serde")]
1523 #[test]
1524 fn test_field_without_metadata_serde() {
1525 let field = Field::new("name", DataType::Boolean, true);
1526 assert_binary_serde_round_trip(field)
1527 }
1528
1529 #[cfg(feature = "serde")]
1530 #[test]
1531 fn test_field_with_empty_metadata_serde() {
1532 let field = Field::new("name", DataType::Boolean, false).with_metadata(HashMap::new());
1533
1534 assert_binary_serde_round_trip(field)
1535 }
1536
1537 #[cfg(feature = "serde")]
1538 #[test]
1539 fn test_field_with_nonempty_metadata_serde() {
1540 let mut metadata = HashMap::new();
1541 metadata.insert("hi".to_owned(), String::new());
1542 let field = Field::new("name", DataType::Boolean, false).with_metadata(metadata);
1543
1544 assert_binary_serde_round_trip(field)
1545 }
1546
1547 #[test]
1548 fn test_merge_compound_with_null() {
1549 let mut field = Field::new(
1551 "s",
1552 DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int32, false)])),
1553 false,
1554 );
1555 field
1556 .try_merge(&Field::new("s", DataType::Null, true))
1557 .expect("Struct should merge with Null");
1558 assert!(field.is_nullable());
1559 assert!(matches!(field.data_type(), DataType::Struct(_)));
1560
1561 let mut field = Field::new(
1563 "l",
1564 DataType::List(Field::new("item", DataType::Utf8, false).into()),
1565 false,
1566 );
1567 field
1568 .try_merge(&Field::new("l", DataType::Null, true))
1569 .expect("List should merge with Null");
1570 assert!(field.is_nullable());
1571 assert!(matches!(field.data_type(), DataType::List(_)));
1572
1573 let mut field = Field::new(
1575 "ll",
1576 DataType::LargeList(Field::new("item", DataType::Utf8, false).into()),
1577 false,
1578 );
1579 field
1580 .try_merge(&Field::new("ll", DataType::Null, true))
1581 .expect("LargeList should merge with Null");
1582 assert!(field.is_nullable());
1583 assert!(matches!(field.data_type(), DataType::LargeList(_)));
1584
1585 let mut field = Field::new(
1587 "u",
1588 DataType::Union(
1589 UnionFields::try_new(vec![0], vec![Field::new("f", DataType::Int32, false)])
1590 .unwrap(),
1591 UnionMode::Dense,
1592 ),
1593 false,
1594 );
1595 field
1596 .try_merge(&Field::new("u", DataType::Null, true))
1597 .expect("Union should merge with Null");
1598 assert!(matches!(field.data_type(), DataType::Union(_, _)));
1599 }
1600}