1use crate::error::ArrowError;
19use std::cmp::Ordering;
20use std::hash::{Hash, Hasher};
21use std::sync::Arc;
22
23use crate::datatype::DataType;
24#[cfg(feature = "canonical_extension_types")]
25use crate::extension::CanonicalExtensionType;
26use crate::schema::SchemaBuilder;
27use crate::{
28 Fields, Metadata, UnionFields, UnionMode,
29 extension::{EXTENSION_TYPE_METADATA_KEY, EXTENSION_TYPE_NAME_KEY, ExtensionType},
30};
31
32pub type FieldRef = Arc<Field>;
34
35#[derive(Clone)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct Field {
49 name: String,
50 data_type: DataType,
51 nullable: bool,
52 #[deprecated(
53 since = "54.0.0",
54 note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
55 )]
56 dict_id: i64,
57 dict_is_ordered: bool,
58 metadata: Metadata,
60}
61
62impl std::fmt::Debug for Field {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 #![expect(deprecated)] let Self {
66 name,
67 data_type,
68 nullable,
69 dict_id,
70 dict_is_ordered,
71 metadata,
72 } = self;
73
74 let mut s = f.debug_struct("Field");
75
76 if name != "item" {
77 s.field("name", name);
79 }
80
81 s.field("data_type", data_type);
82
83 if *nullable {
84 s.field("nullable", nullable);
85 }
86
87 if *dict_id != 0 {
88 s.field("dict_id", dict_id);
89 }
90
91 if *dict_is_ordered {
92 s.field("dict_is_ordered", dict_is_ordered);
93 }
94
95 if !metadata.is_empty() {
96 s.field("metadata", metadata);
97 }
98 s.finish()
99 }
100}
101
102impl PartialEq for Field {
108 fn eq(&self, other: &Self) -> bool {
109 self.name == other.name
110 && self.data_type == other.data_type
111 && self.nullable == other.nullable
112 && self.metadata == other.metadata
113 }
114}
115
116impl Eq for Field {}
117
118impl PartialOrd for Field {
119 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
120 Some(self.cmp(other))
121 }
122}
123
124impl Ord for Field {
125 fn cmp(&self, other: &Self) -> Ordering {
126 self.name
127 .cmp(other.name())
128 .then_with(|| self.data_type.cmp(other.data_type()))
129 .then_with(|| self.nullable.cmp(&other.nullable))
130 .then_with(|| self.metadata.cmp(&other.metadata))
131 }
132}
133
134impl Hash for Field {
135 fn hash<H: Hasher>(&self, state: &mut H) {
136 self.name.hash(state);
137 self.data_type.hash(state);
138 self.nullable.hash(state);
139 self.metadata.hash(state);
141 }
142}
143
144impl AsRef<Field> for Field {
145 fn as_ref(&self) -> &Field {
146 self
147 }
148}
149
150impl Field {
151 pub const LIST_FIELD_DEFAULT_NAME: &'static str = "item";
153 pub const MAP_ENTRIES_FIELD_DEFAULT_NAME: &'static str = "entries";
157 pub const MAP_KEY_FIELD_DEFAULT_NAME: &'static str = "key";
161 pub const MAP_VALUE_FIELD_DEFAULT_NAME: &'static str = "value";
165 pub const REE_RUN_ENDS_FIELD_DEFAULT_NAME: &'static str = "run_ends";
169 pub const REE_VALUES_FIELD_DEFAULT_NAME: &'static str = "values";
173
174 pub fn new(name: impl Into<String>, data_type: DataType, nullable: bool) -> Self {
182 #[expect(deprecated)]
183 Field {
184 name: name.into(),
185 data_type,
186 nullable,
187 dict_id: 0,
188 dict_is_ordered: false,
189 metadata: Default::default(),
190 }
191 }
192
193 pub fn new_list_field(data_type: DataType, nullable: bool) -> Self {
208 Self::new(Self::LIST_FIELD_DEFAULT_NAME, data_type, nullable)
209 }
210
211 #[deprecated(
213 since = "54.0.0",
214 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."
215 )]
216 pub fn new_dict(
217 name: impl Into<String>,
218 data_type: DataType,
219 nullable: bool,
220 dict_id: i64,
221 dict_is_ordered: bool,
222 ) -> Self {
223 #[expect(deprecated)]
224 Field {
225 name: name.into(),
226 data_type,
227 nullable,
228 dict_id,
229 dict_is_ordered,
230 metadata: Default::default(),
231 }
232 }
233
234 pub fn new_dictionary(
242 name: impl Into<String>,
243 key: DataType,
244 value: DataType,
245 nullable: bool,
246 ) -> Self {
247 assert!(
248 key.is_dictionary_key_type(),
249 "{key} is not a valid dictionary key"
250 );
251 let data_type = DataType::Dictionary(Box::new(key), Box::new(value));
252 Self::new(name, data_type, nullable)
253 }
254
255 pub fn new_struct(name: impl Into<String>, fields: impl Into<Fields>, nullable: bool) -> Self {
261 Self::new(name, DataType::Struct(fields.into()), nullable)
262 }
263
264 pub fn new_list(name: impl Into<String>, value: impl Into<FieldRef>, nullable: bool) -> Self {
270 Self::new(name, DataType::List(value.into()), nullable)
271 }
272
273 pub fn new_large_list(
279 name: impl Into<String>,
280 value: impl Into<FieldRef>,
281 nullable: bool,
282 ) -> Self {
283 Self::new(name, DataType::LargeList(value.into()), nullable)
284 }
285
286 pub fn new_fixed_size_list(
293 name: impl Into<String>,
294 value: impl Into<FieldRef>,
295 size: i32,
296 nullable: bool,
297 ) -> Self {
298 Self::new(name, DataType::FixedSizeList(value.into(), size), nullable)
299 }
300
301 pub fn new_map(
310 name: impl Into<String>,
311 entries: impl Into<String>,
312 keys: impl Into<FieldRef>,
313 values: impl Into<FieldRef>,
314 sorted: bool,
315 nullable: bool,
316 ) -> Self {
317 let data_type = DataType::Map(
318 Arc::new(Field::new(
319 entries.into(),
320 DataType::Struct(Fields::from([keys.into(), values.into()])),
321 false, )),
323 sorted,
324 );
325 Self::new(name, data_type, nullable)
326 }
327
328 pub fn new_union<S, F, T>(name: S, type_ids: T, fields: F, mode: UnionMode) -> Self
342 where
343 S: Into<String>,
344 F: IntoIterator,
345 F::Item: Into<FieldRef>,
346 T: IntoIterator<Item = i8>,
347 {
348 Self::new(
349 name,
350 DataType::Union(
351 UnionFields::try_new(type_ids, fields).expect("Invalid UnionField"),
352 mode,
353 ),
354 false, )
356 }
357
358 #[inline]
360 pub fn set_metadata(&mut self, metadata: impl Into<Metadata>) {
361 self.metadata = metadata.into();
362 }
363
364 pub fn with_metadata(mut self, metadata: impl Into<Metadata>) -> Self {
366 self.set_metadata(metadata);
367 self
368 }
369
370 #[inline]
372 pub const fn metadata(&self) -> &Metadata {
373 &self.metadata
374 }
375
376 #[inline]
378 pub fn metadata_mut(&mut self) -> &mut Metadata {
379 &mut self.metadata
380 }
381
382 #[inline]
384 pub const fn name(&self) -> &String {
385 &self.name
386 }
387
388 #[inline]
390 pub fn set_name(&mut self, name: impl Into<String>) {
391 self.name = name.into();
392 }
393
394 pub fn with_name(mut self, name: impl Into<String>) -> Self {
404 self.set_name(name);
405 self
406 }
407
408 #[inline]
410 pub const fn data_type(&self) -> &DataType {
411 &self.data_type
412 }
413
414 #[inline]
424 pub fn set_data_type(&mut self, data_type: DataType) {
425 self.data_type = data_type;
426 }
427
428 pub fn with_data_type(mut self, data_type: DataType) -> Self {
438 self.set_data_type(data_type);
439 self
440 }
441
442 pub fn extension_type_name(&self) -> Option<&str> {
461 self.metadata()
462 .get(EXTENSION_TYPE_NAME_KEY)
463 .map(String::as_ref)
464 }
465
466 pub fn extension_type_metadata(&self) -> Option<&str> {
485 self.metadata()
486 .get(EXTENSION_TYPE_METADATA_KEY)
487 .map(String::as_ref)
488 }
489
490 #[inline]
499 pub fn has_valid_extension_type<E: ExtensionType>(&self) -> bool {
500 if self.extension_type_name() != Some(E::NAME) {
501 return false;
502 }
503
504 let ext_metadata = self
505 .metadata()
506 .get(EXTENSION_TYPE_METADATA_KEY)
507 .map(|s| s.as_str());
508
509 E::deserialize_metadata(ext_metadata)
510 .and_then(|metadata| E::validate(self.data_type(), metadata))
511 .is_ok()
512 }
513
514 pub fn try_extension_type<E: ExtensionType>(&self) -> Result<E, ArrowError> {
559 E::try_new_from_field_metadata(self.data_type(), self.metadata())
560 }
561
562 pub fn extension_type<E: ExtensionType>(&self) -> E {
570 self.try_extension_type::<E>()
571 .unwrap_or_else(|e| panic!("{e}"))
572 }
573
574 pub fn try_with_extension_type<E: ExtensionType>(
587 &mut self,
588 extension_type: E,
589 ) -> Result<(), ArrowError> {
590 extension_type.supports_data_type(&self.data_type)?;
592
593 self.metadata
594 .insert(EXTENSION_TYPE_NAME_KEY.to_owned(), E::NAME.to_owned());
595 match extension_type.serialize_metadata() {
596 Some(metadata) => self
597 .metadata
598 .insert(EXTENSION_TYPE_METADATA_KEY.to_owned(), metadata),
599 None => self.metadata.remove(EXTENSION_TYPE_METADATA_KEY),
602 };
603
604 Ok(())
605 }
606
607 pub fn with_extension_type<E: ExtensionType>(mut self, extension_type: E) -> Self {
615 self.try_with_extension_type(extension_type)
616 .unwrap_or_else(|e| panic!("{e}"));
617 self
618 }
619
620 #[cfg(feature = "canonical_extension_types")]
629 pub fn try_canonical_extension_type(&self) -> Result<CanonicalExtensionType, ArrowError> {
630 CanonicalExtensionType::try_from(self)
631 }
632
633 #[inline]
637 pub const fn is_nullable(&self) -> bool {
638 self.nullable
639 }
640
641 #[inline]
651 pub fn set_nullable(&mut self, nullable: bool) {
652 self.nullable = nullable;
653 }
654
655 pub fn with_nullable(mut self, nullable: bool) -> Self {
665 self.set_nullable(nullable);
666 self
667 }
668
669 pub(crate) fn fields(&self) -> Vec<&Field> {
672 let mut collected_fields = vec![self];
673 collected_fields.append(&mut Field::_fields(&self.data_type));
674
675 collected_fields
676 }
677
678 fn _fields(dt: &DataType) -> Vec<&Field> {
679 match dt {
680 DataType::Struct(fields) => fields.iter().flat_map(|f| f.fields()).collect(),
681 DataType::Union(fields, _) => fields.iter().flat_map(|(_, f)| f.fields()).collect(),
682 DataType::List(field)
683 | DataType::LargeList(field)
684 | DataType::ListView(field)
685 | DataType::LargeListView(field)
686 | DataType::FixedSizeList(field, _)
687 | DataType::Map(field, _) => field.fields(),
688 DataType::Dictionary(_, value_field) => Field::_fields(value_field.as_ref()),
689 DataType::RunEndEncoded(_, field) => field.fields(),
690 _ => vec![],
691 }
692 }
693
694 #[inline]
697 #[deprecated(
698 since = "54.0.0",
699 note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
700 )]
701 pub(crate) fn fields_with_dict_id(&self, id: i64) -> Vec<&Field> {
702 self.fields()
703 .into_iter()
704 .filter(|&field| {
705 #[expect(deprecated)]
706 let matching_dict_id = field.dict_id == id;
707 matches!(field.data_type(), DataType::Dictionary(_, _)) && matching_dict_id
708 })
709 .collect()
710 }
711
712 #[inline]
714 #[deprecated(
715 since = "54.0.0",
716 note = "The ability to preserve dictionary IDs will be removed. With it, all fields related to it."
717 )]
718 pub const fn dict_id(&self) -> Option<i64> {
719 match self.data_type {
720 #[expect(deprecated)]
721 DataType::Dictionary(_, _) => Some(self.dict_id),
722 _ => None,
723 }
724 }
725
726 #[inline]
741 pub const fn dict_is_ordered(&self) -> Option<bool> {
742 match self.data_type {
743 DataType::Dictionary(_, _) => Some(self.dict_is_ordered),
744 _ => None,
745 }
746 }
747
748 pub fn with_dict_is_ordered(mut self, dict_is_ordered: bool) -> Self {
754 if matches!(self.data_type, DataType::Dictionary(_, _)) {
755 self.dict_is_ordered = dict_is_ordered;
756 }
757 self
758 }
759
760 pub fn try_merge(&mut self, from: &Field) -> Result<(), ArrowError> {
775 if from.dict_is_ordered != self.dict_is_ordered {
776 return Err(ArrowError::SchemaError(format!(
777 "Fail to merge schema field '{}' because from dict_is_ordered = {} does not match {}",
778 self.name, from.dict_is_ordered, self.dict_is_ordered
779 )));
780 }
781 match (self.metadata().is_empty(), from.metadata().is_empty()) {
783 (false, false) => {
784 let mut merged = self.metadata().clone();
785 for (key, from_value) in from.metadata() {
786 if let Some(self_value) = self.metadata.get(key) {
787 if self_value != from_value {
788 return Err(ArrowError::SchemaError(format!(
789 "Fail to merge field '{}' due to conflicting metadata data value for key {}.
790 From value = {} does not match {}", self.name, key, from_value, self_value),
791 ));
792 }
793 } else {
794 merged.insert(key.clone(), from_value.clone());
795 }
796 }
797 self.set_metadata(merged);
798 }
799 (true, false) => {
800 self.set_metadata(from.metadata().clone());
801 }
802 _ => {}
803 }
804 match &mut self.data_type {
805 DataType::Struct(nested_fields) => match &from.data_type {
806 DataType::Struct(from_nested_fields) => {
807 let mut builder = SchemaBuilder::new();
808 nested_fields
809 .iter()
810 .chain(from_nested_fields)
811 .try_for_each(|f| builder.try_merge(f))?;
812 *nested_fields = builder.finish().fields;
813 }
814 DataType::Null => {
815 self.nullable = true;
816 }
817 _ => {
818 return Err(ArrowError::SchemaError(format!(
819 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::Struct",
820 self.name, from.data_type
821 )));
822 }
823 },
824 DataType::Union(nested_fields, mode) => match &from.data_type {
825 DataType::Union(from_nested_fields, from_mode) => {
826 if mode != from_mode {
827 return Err(ArrowError::SchemaError(format!(
828 "Fail to merge schema field '{}' because the from union mode = {:?} does not match {:?}",
829 self.name, from_mode, mode
830 )));
831 }
832 nested_fields.try_merge(from_nested_fields)?;
833 }
834 DataType::Null => {
835 self.nullable = true;
836 }
837 _ => {
838 return Err(ArrowError::SchemaError(format!(
839 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::Union",
840 self.name, from.data_type
841 )));
842 }
843 },
844 DataType::List(field) => match &from.data_type {
845 DataType::List(from_field) => {
846 let mut f = (**field).clone();
847 f.try_merge(from_field)?;
848 (*field) = Arc::new(f);
849 }
850 DataType::Null => {
851 self.nullable = true;
852 }
853 _ => {
854 return Err(ArrowError::SchemaError(format!(
855 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::List",
856 self.name, from.data_type
857 )));
858 }
859 },
860 DataType::LargeList(field) => match &from.data_type {
861 DataType::LargeList(from_field) => {
862 let mut f = (**field).clone();
863 f.try_merge(from_field)?;
864 (*field) = Arc::new(f);
865 }
866 DataType::Null => {
867 self.nullable = true;
868 }
869 _ => {
870 return Err(ArrowError::SchemaError(format!(
871 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::LargeList",
872 self.name, from.data_type
873 )));
874 }
875 },
876 DataType::Null => {
877 self.nullable = true;
878 self.data_type = from.data_type.clone();
879 }
880 DataType::Boolean
881 | DataType::Int8
882 | DataType::Int16
883 | DataType::Int32
884 | DataType::Int64
885 | DataType::UInt8
886 | DataType::UInt16
887 | DataType::UInt32
888 | DataType::UInt64
889 | DataType::Float16
890 | DataType::Float32
891 | DataType::Float64
892 | DataType::Timestamp(_, _)
893 | DataType::Date32
894 | DataType::Date64
895 | DataType::Time32(_)
896 | DataType::Time64(_)
897 | DataType::Duration(_)
898 | DataType::Binary
899 | DataType::LargeBinary
900 | DataType::BinaryView
901 | DataType::Interval(_)
902 | DataType::LargeListView(_)
903 | DataType::ListView(_)
904 | DataType::Map(_, _)
905 | DataType::Dictionary(_, _)
906 | DataType::RunEndEncoded(_, _)
907 | DataType::FixedSizeList(_, _)
908 | DataType::FixedSizeBinary(_)
909 | DataType::Utf8
910 | DataType::LargeUtf8
911 | DataType::Utf8View
912 | DataType::Decimal32(_, _)
913 | DataType::Decimal64(_, _)
914 | DataType::Decimal128(_, _)
915 | DataType::Decimal256(_, _) => {
916 if from.data_type == DataType::Null {
917 self.nullable = true;
918 } else if self.data_type != from.data_type {
919 return Err(ArrowError::SchemaError(format!(
920 "Fail to merge schema field '{}' because the from data_type = {} does not equal {}",
921 self.name, from.data_type, self.data_type
922 )));
923 }
924 }
925 }
926 self.nullable |= from.nullable;
927
928 Ok(())
929 }
930
931 pub fn contains(&self, other: &Field) -> bool {
937 self.name == other.name
938 && self.data_type.contains(&other.data_type)
939 && self.dict_is_ordered == other.dict_is_ordered
940 && (self.nullable || !other.nullable)
942 && other.metadata.iter().all(|(k, v1)| {
944 self.metadata.get(k).is_some_and(|v2| v1 == v2)
945 })
946 }
947
948 pub fn size(&self) -> usize {
952 std::mem::size_of_val(self) - std::mem::size_of_val(&self.data_type)
953 + self.data_type.size()
954 + self.name.capacity()
955 + (std::mem::size_of::<(String, String)>() * self.metadata.len())
956 + self
957 .metadata
958 .iter()
959 .map(|(k, v)| k.capacity() + v.capacity())
960 .sum::<usize>()
961 }
962}
963
964impl std::fmt::Display for Field {
965 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
966 #![expect(deprecated)] let Self {
968 name,
969 data_type,
970 nullable,
971 dict_id,
972 dict_is_ordered,
973 metadata,
974 } = self;
975 let maybe_nullable = if *nullable { "nullable " } else { "" };
976 let metadata_str = if metadata.is_empty() {
977 String::new()
978 } else {
979 format!(", metadata: {metadata:?}")
980 };
981 let dict_id_str = if dict_id == &0 {
982 String::new()
983 } else {
984 format!(", dict_id: {dict_id}")
985 };
986 let dict_is_ordered_str = if *dict_is_ordered {
987 ", dict_is_ordered"
988 } else {
989 ""
990 };
991 write!(
992 f,
993 "Field {{ {name:?}: {maybe_nullable}{data_type}{dict_id_str}{dict_is_ordered_str}{metadata_str} }}"
994 )
995 }
996}
997
998#[cfg(test)]
999mod test {
1000 use super::*;
1001 use std::collections::HashMap;
1002 use std::collections::hash_map::DefaultHasher;
1003
1004 #[derive(Debug, Clone, Copy)]
1005 struct TestExtensionType;
1006
1007 impl ExtensionType for TestExtensionType {
1008 const NAME: &'static str = "test.extension";
1009 type Metadata = ();
1010
1011 fn metadata(&self) -> &Self::Metadata {
1012 &()
1013 }
1014
1015 fn serialize_metadata(&self) -> Option<String> {
1016 None
1017 }
1018
1019 fn deserialize_metadata(metadata: Option<&str>) -> Result<Self::Metadata, ArrowError> {
1020 metadata.map_or(Ok(()), |_| {
1021 Err(ArrowError::InvalidArgumentError(
1022 "TestExtensionType expects no metadata".to_owned(),
1023 ))
1024 })
1025 }
1026
1027 fn supports_data_type(&self, _data_type: &DataType) -> Result<(), ArrowError> {
1028 Ok(())
1029 }
1030
1031 fn try_new(_data_type: &DataType, _metadata: Self::Metadata) -> Result<Self, ArrowError> {
1032 Ok(Self)
1033 }
1034 }
1035
1036 #[test]
1037 fn test_has_valid_extension_type() {
1038 let no_extension = Field::new("f", DataType::Null, false);
1039 assert!(!no_extension.has_valid_extension_type::<TestExtensionType>());
1040
1041 let matching_name = Field::new("f", DataType::Null, false)
1042 .with_metadata([(EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME)]);
1043 assert!(matching_name.has_valid_extension_type::<TestExtensionType>());
1044
1045 let matching_name_with_invalid_metadata = Field::new("f", DataType::Null, false)
1046 .with_metadata([
1047 (EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME),
1048 (EXTENSION_TYPE_METADATA_KEY, "invalid"),
1049 ]);
1050 assert!(
1051 !matching_name_with_invalid_metadata.has_valid_extension_type::<TestExtensionType>()
1052 );
1053
1054 let different_name = Field::new("f", DataType::Null, false)
1055 .with_metadata([(EXTENSION_TYPE_NAME_KEY, "some.other_extension")]);
1056 assert!(!different_name.has_valid_extension_type::<TestExtensionType>());
1057 }
1058
1059 #[test]
1060 fn test_new_with_string() {
1061 let s = "c1";
1063 Field::new(s, DataType::Int64, false);
1064 }
1065
1066 #[test]
1067 fn test_new_dict_with_string() {
1068 let s = "c1";
1070 #[expect(deprecated)]
1071 Field::new_dict(s, DataType::Int64, false, 4, false);
1072 }
1073
1074 #[test]
1075 #[cfg_attr(miri, ignore)] fn test_debug_format_field() {
1077 insta::assert_debug_snapshot!(Field::new("item", DataType::UInt8, false), @r"
1079 Field {
1080 data_type: UInt8,
1081 }
1082 ");
1083 insta::assert_debug_snapshot!(Field::new("column", DataType::LargeUtf8, true), @r#"
1084 Field {
1085 name: "column",
1086 data_type: LargeUtf8,
1087 nullable: true,
1088 }
1089 "#);
1090 }
1091
1092 #[test]
1093 fn test_merge_union_modes() {
1094 for mode in [UnionMode::Sparse, UnionMode::Dense] {
1095 let mut field = Field::new_union(
1096 "u",
1097 [0],
1098 [Field::new("number", DataType::Int32, false)],
1099 mode,
1100 );
1101 let other_mode = match mode {
1102 UnionMode::Sparse => UnionMode::Dense,
1103 UnionMode::Dense => UnionMode::Sparse,
1104 };
1105 let incompatible = Field::new_union(
1106 "u",
1107 [0],
1108 [Field::new("number", DataType::Int32, false)],
1109 other_mode,
1110 );
1111 assert!(matches!(
1112 field.try_merge(&incompatible),
1113 Err(ArrowError::SchemaError(_))
1114 ));
1115
1116 let compatible =
1117 Field::new_union("u", [1], [Field::new("text", DataType::Utf8, false)], mode);
1118 field.try_merge(&compatible).unwrap();
1119 let expected = Field::new_union(
1120 "u",
1121 [0, 1],
1122 [
1123 Field::new("number", DataType::Int32, false),
1124 Field::new("text", DataType::Utf8, false),
1125 ],
1126 mode,
1127 );
1128 assert_eq!(field, expected);
1129 }
1130 }
1131
1132 #[test]
1133 fn test_merge_incompatible_types() {
1134 let mut field = Field::new("c1", DataType::Int64, false);
1135 let result = field
1136 .try_merge(&Field::new("c1", DataType::Float32, true))
1137 .expect_err("should fail")
1138 .to_string();
1139 assert_eq!(
1140 "Schema error: Fail to merge schema field 'c1' because the from data_type = Float32 does not equal Int64",
1141 result
1142 );
1143 }
1144
1145 #[test]
1146 fn test_merge_with_null() {
1147 let mut field1 = Field::new("c1", DataType::Null, true);
1148 field1
1149 .try_merge(&Field::new("c1", DataType::Float32, false))
1150 .expect("should widen type to nullable float");
1151 assert_eq!(Field::new("c1", DataType::Float32, true), field1);
1152
1153 let mut field2 = Field::new("c2", DataType::Utf8, false);
1154 field2
1155 .try_merge(&Field::new("c2", DataType::Null, true))
1156 .expect("should widen type to nullable utf8");
1157 assert_eq!(Field::new("c2", DataType::Utf8, true), field2);
1158 }
1159
1160 #[test]
1161 fn test_merge_with_nested_null() {
1162 let mut struct1 = Field::new(
1163 "s1",
1164 DataType::Struct(Fields::from(vec![Field::new(
1165 "inner",
1166 DataType::Float32,
1167 false,
1168 )])),
1169 false,
1170 );
1171
1172 let struct2 = Field::new(
1173 "s2",
1174 DataType::Struct(Fields::from(vec![Field::new(
1175 "inner",
1176 DataType::Null,
1177 false,
1178 )])),
1179 true,
1180 );
1181
1182 struct1
1183 .try_merge(&struct2)
1184 .expect("should widen inner field's type to nullable float");
1185 assert_eq!(
1186 Field::new(
1187 "s1",
1188 DataType::Struct(Fields::from(vec![Field::new(
1189 "inner",
1190 DataType::Float32,
1191 true,
1192 )])),
1193 true,
1194 ),
1195 struct1
1196 );
1197
1198 let mut list1 = Field::new(
1199 "l1",
1200 DataType::List(Field::new("inner", DataType::Float32, false).into()),
1201 false,
1202 );
1203
1204 let list2 = Field::new(
1205 "l2",
1206 DataType::List(Field::new("inner", DataType::Null, false).into()),
1207 true,
1208 );
1209
1210 list1
1211 .try_merge(&list2)
1212 .expect("should widen inner field's type to nullable float");
1213 assert_eq!(
1214 Field::new(
1215 "l1",
1216 DataType::List(Field::new("inner", DataType::Float32, true).into()),
1217 true,
1218 ),
1219 list1
1220 );
1221
1222 let mut large_list1 = Field::new(
1223 "ll1",
1224 DataType::LargeList(Field::new("inner", DataType::Float32, false).into()),
1225 false,
1226 );
1227
1228 let large_list2 = Field::new(
1229 "ll2",
1230 DataType::LargeList(Field::new("inner", DataType::Null, false).into()),
1231 true,
1232 );
1233
1234 large_list1
1235 .try_merge(&large_list2)
1236 .expect("should widen inner field's type to nullable float");
1237 assert_eq!(
1238 Field::new(
1239 "ll1",
1240 DataType::LargeList(Field::new("inner", DataType::Float32, true).into()),
1241 true,
1242 ),
1243 large_list1
1244 );
1245 }
1246
1247 #[test]
1248 fn test_fields_with_dict_id() {
1249 #[expect(deprecated)]
1250 let dict1 = Field::new_dict(
1251 "dict1",
1252 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1253 false,
1254 10,
1255 false,
1256 );
1257 #[expect(deprecated)]
1258 let dict2 = Field::new_dict(
1259 "dict2",
1260 DataType::Dictionary(DataType::Int32.into(), DataType::Int8.into()),
1261 false,
1262 20,
1263 false,
1264 );
1265
1266 let field = Field::new(
1267 "struct<dict1, list[struct<dict2, list[struct<dict1]>]>",
1268 DataType::Struct(Fields::from(vec![
1269 dict1.clone(),
1270 Field::new(
1271 "list[struct<dict1, list[struct<dict2>]>]",
1272 DataType::List(Arc::new(Field::new(
1273 "struct<dict1, list[struct<dict2>]>",
1274 DataType::Struct(Fields::from(vec![
1275 dict1.clone(),
1276 Field::new(
1277 "list[struct<dict2>]",
1278 DataType::List(Arc::new(Field::new(
1279 "struct<dict2>",
1280 DataType::Struct(vec![dict2.clone()].into()),
1281 false,
1282 ))),
1283 false,
1284 ),
1285 ])),
1286 false,
1287 ))),
1288 false,
1289 ),
1290 ])),
1291 false,
1292 );
1293
1294 #[expect(deprecated)]
1295 for field in field.fields_with_dict_id(10) {
1296 assert_eq!(dict1, *field);
1297 }
1298 #[expect(deprecated)]
1299 for field in field.fields_with_dict_id(20) {
1300 assert_eq!(dict2, *field);
1301 }
1302 }
1303
1304 fn get_field_hash(field: &Field) -> u64 {
1305 let mut s = DefaultHasher::new();
1306 field.hash(&mut s);
1307 s.finish()
1308 }
1309
1310 #[test]
1311 fn test_field_comparison_case() {
1312 #[expect(deprecated)]
1314 let dict1 = Field::new_dict(
1315 "dict1",
1316 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1317 false,
1318 10,
1319 false,
1320 );
1321 #[expect(deprecated)]
1322 let dict2 = Field::new_dict(
1323 "dict1",
1324 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1325 false,
1326 20,
1327 false,
1328 );
1329
1330 assert_eq!(dict1, dict2);
1331 assert_eq!(get_field_hash(&dict1), get_field_hash(&dict2));
1332
1333 #[expect(deprecated)]
1334 let dict1 = Field::new_dict(
1335 "dict0",
1336 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1337 false,
1338 10,
1339 false,
1340 );
1341
1342 assert_ne!(dict1, dict2);
1343 assert_ne!(get_field_hash(&dict1), get_field_hash(&dict2));
1344 }
1345
1346 #[test]
1347 fn test_field_comparison_metadata() {
1348 let f1 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1349 (String::from("k1"), String::from("v1")),
1350 (String::from("k2"), String::from("v2")),
1351 ]));
1352 let f2 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1353 (String::from("k1"), String::from("v1")),
1354 (String::from("k3"), String::from("v3")),
1355 ]));
1356 let f3 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1357 (String::from("k1"), String::from("v1")),
1358 (String::from("k3"), String::from("v4")),
1359 ]));
1360
1361 assert!(f1.cmp(&f2).is_lt());
1362 assert!(f2.cmp(&f3).is_lt());
1363 assert!(f1.cmp(&f3).is_lt());
1364 }
1365
1366 #[test]
1367 #[expect(clippy::needless_borrows_for_generic_args)] fn test_field_as_ref() {
1369 let field = || Field::new("x", DataType::Binary, false);
1370
1371 fn accept_ref(_: impl AsRef<Field>) {}
1374
1375 accept_ref(field());
1376 accept_ref(&field());
1377 accept_ref(&&field());
1378 accept_ref(Arc::new(field()));
1379 accept_ref(&Arc::new(field()));
1380 accept_ref(&&Arc::new(field()));
1381
1382 fn accept_refs(_: impl IntoIterator<Item: AsRef<Field>>) {}
1385
1386 accept_refs(vec![field()]);
1387 accept_refs(vec![&field()]);
1388 accept_refs(vec![Arc::new(field())]);
1389 accept_refs(vec![&Arc::new(field())]);
1390 accept_refs(&vec![field()]);
1391 accept_refs(&vec![&field()]);
1392 accept_refs(&vec![Arc::new(field())]);
1393 accept_refs(&vec![&Arc::new(field())]);
1394 }
1395
1396 #[test]
1397 fn test_contains_reflexivity() {
1398 let mut field = Field::new("field1", DataType::Float16, false);
1399 field.set_metadata(HashMap::from([
1400 (String::from("k0"), String::from("v0")),
1401 (String::from("k1"), String::from("v1")),
1402 ]));
1403 assert!(field.contains(&field))
1404 }
1405
1406 #[test]
1407 fn test_contains_transitivity() {
1408 let child_field = Field::new("child1", DataType::Float16, false);
1409
1410 let mut field1 = Field::new(
1411 "field1",
1412 DataType::Struct(Fields::from(vec![child_field])),
1413 false,
1414 );
1415 field1.set_metadata(HashMap::from([(String::from("k1"), String::from("v1"))]));
1416
1417 let mut field2 = Field::new("field1", DataType::Struct(Fields::default()), true);
1418 field2.set_metadata(HashMap::from([(String::from("k2"), String::from("v2"))]));
1419 field2.try_merge(&field1).unwrap();
1420
1421 let mut field3 = Field::new("field1", DataType::Struct(Fields::default()), false);
1422 field3.set_metadata(HashMap::from([(String::from("k3"), String::from("v3"))]));
1423 field3.try_merge(&field2).unwrap();
1424
1425 assert!(field2.contains(&field1));
1426 assert!(field3.contains(&field2));
1427 assert!(field3.contains(&field1));
1428
1429 assert!(!field1.contains(&field2));
1430 assert!(!field1.contains(&field3));
1431 assert!(!field2.contains(&field3));
1432 }
1433
1434 #[test]
1435 fn test_contains_nullable() {
1436 let field1 = Field::new("field1", DataType::Boolean, true);
1437 let field2 = Field::new("field1", DataType::Boolean, false);
1438 assert!(field1.contains(&field2));
1439 assert!(!field2.contains(&field1));
1440 }
1441
1442 #[test]
1443 fn test_contains_must_have_same_fields() {
1444 let child_field1 = Field::new("child1", DataType::Float16, false);
1445 let child_field2 = Field::new("child2", DataType::Float16, false);
1446
1447 let field1 = Field::new(
1448 "field1",
1449 DataType::Struct(vec![child_field1.clone()].into()),
1450 true,
1451 );
1452 let field2 = Field::new(
1453 "field1",
1454 DataType::Struct(vec![child_field1, child_field2].into()),
1455 true,
1456 );
1457
1458 assert!(!field1.contains(&field2));
1459 assert!(!field2.contains(&field1));
1460
1461 let field1 = Field::new(
1463 "field1",
1464 DataType::Union(
1465 UnionFields::try_new(
1466 vec![1, 2],
1467 vec![
1468 Field::new("field1", DataType::UInt8, true),
1469 Field::new("field3", DataType::Utf8, false),
1470 ],
1471 )
1472 .unwrap(),
1473 UnionMode::Dense,
1474 ),
1475 true,
1476 );
1477 let field2 = Field::new(
1478 "field1",
1479 DataType::Union(
1480 UnionFields::try_new(
1481 vec![1, 3],
1482 vec![
1483 Field::new("field1", DataType::UInt8, false),
1484 Field::new("field3", DataType::Utf8, false),
1485 ],
1486 )
1487 .unwrap(),
1488 UnionMode::Dense,
1489 ),
1490 true,
1491 );
1492 assert!(!field1.contains(&field2));
1493
1494 let field1 = Field::new(
1496 "field1",
1497 DataType::Union(
1498 UnionFields::try_new(
1499 vec![1, 2],
1500 vec![
1501 Field::new("field1", DataType::UInt8, true),
1502 Field::new("field3", DataType::Utf8, false),
1503 ],
1504 )
1505 .unwrap(),
1506 UnionMode::Dense,
1507 ),
1508 true,
1509 );
1510 let field2 = Field::new(
1511 "field1",
1512 DataType::Union(
1513 UnionFields::try_new(
1514 vec![1, 2],
1515 vec![
1516 Field::new("field1", DataType::UInt8, false),
1517 Field::new("field3", DataType::Utf8, false),
1518 ],
1519 )
1520 .unwrap(),
1521 UnionMode::Dense,
1522 ),
1523 true,
1524 );
1525 assert!(field1.contains(&field2));
1526 }
1527
1528 #[cfg(feature = "serde")]
1529 fn assert_binary_serde_round_trip(field: Field) {
1530 let serialized = postcard::to_stdvec(&field).unwrap();
1531 let deserialized: Field = postcard::from_bytes(&serialized).unwrap();
1532 assert_eq!(field, deserialized)
1533 }
1534
1535 #[cfg(feature = "serde")]
1536 #[test]
1537 fn test_field_without_metadata_serde() {
1538 let field = Field::new("name", DataType::Boolean, true);
1539 assert_binary_serde_round_trip(field)
1540 }
1541
1542 #[cfg(feature = "serde")]
1543 #[test]
1544 fn test_field_with_empty_metadata_serde() {
1545 let field = Field::new("name", DataType::Boolean, false).with_metadata(HashMap::new());
1546
1547 assert_binary_serde_round_trip(field)
1548 }
1549
1550 #[cfg(feature = "serde")]
1551 #[test]
1552 fn test_field_with_nonempty_metadata_serde() {
1553 let mut metadata = HashMap::new();
1554 metadata.insert("hi".to_owned(), String::new());
1555 let field = Field::new("name", DataType::Boolean, false).with_metadata(metadata);
1556
1557 assert_binary_serde_round_trip(field)
1558 }
1559
1560 #[test]
1561 fn test_merge_compound_with_null() {
1562 let mut field = Field::new(
1564 "s",
1565 DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int32, false)])),
1566 false,
1567 );
1568 field
1569 .try_merge(&Field::new("s", DataType::Null, true))
1570 .expect("Struct should merge with Null");
1571 assert!(field.is_nullable());
1572 assert!(matches!(field.data_type(), DataType::Struct(_)));
1573
1574 let mut field = Field::new(
1576 "l",
1577 DataType::List(Field::new("item", DataType::Utf8, false).into()),
1578 false,
1579 );
1580 field
1581 .try_merge(&Field::new("l", DataType::Null, true))
1582 .expect("List should merge with Null");
1583 assert!(field.is_nullable());
1584 assert!(matches!(field.data_type(), DataType::List(_)));
1585
1586 let mut field = Field::new(
1588 "ll",
1589 DataType::LargeList(Field::new("item", DataType::Utf8, false).into()),
1590 false,
1591 );
1592 field
1593 .try_merge(&Field::new("ll", DataType::Null, true))
1594 .expect("LargeList should merge with Null");
1595 assert!(field.is_nullable());
1596 assert!(matches!(field.data_type(), DataType::LargeList(_)));
1597
1598 let mut field = Field::new(
1600 "u",
1601 DataType::Union(
1602 UnionFields::try_new(vec![0], vec![Field::new("f", DataType::Int32, false)])
1603 .unwrap(),
1604 UnionMode::Dense,
1605 ),
1606 false,
1607 );
1608 field
1609 .try_merge(&Field::new("u", DataType::Null, true))
1610 .expect("Union should merge with Null");
1611 assert!(matches!(field.data_type(), DataType::Union(_, _)));
1612 }
1613}