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, _) => match &from.data_type {
825 DataType::Union(from_nested_fields, _) => {
826 nested_fields.try_merge(from_nested_fields)?
827 }
828 DataType::Null => {
829 self.nullable = true;
830 }
831 _ => {
832 return Err(ArrowError::SchemaError(format!(
833 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::Union",
834 self.name, from.data_type
835 )));
836 }
837 },
838 DataType::List(field) => match &from.data_type {
839 DataType::List(from_field) => {
840 let mut f = (**field).clone();
841 f.try_merge(from_field)?;
842 (*field) = Arc::new(f);
843 }
844 DataType::Null => {
845 self.nullable = true;
846 }
847 _ => {
848 return Err(ArrowError::SchemaError(format!(
849 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::List",
850 self.name, from.data_type
851 )));
852 }
853 },
854 DataType::LargeList(field) => match &from.data_type {
855 DataType::LargeList(from_field) => {
856 let mut f = (**field).clone();
857 f.try_merge(from_field)?;
858 (*field) = Arc::new(f);
859 }
860 DataType::Null => {
861 self.nullable = true;
862 }
863 _ => {
864 return Err(ArrowError::SchemaError(format!(
865 "Fail to merge schema field '{}' because the from data_type = {} is not DataType::LargeList",
866 self.name, from.data_type
867 )));
868 }
869 },
870 DataType::Null => {
871 self.nullable = true;
872 self.data_type = from.data_type.clone();
873 }
874 DataType::Boolean
875 | DataType::Int8
876 | DataType::Int16
877 | DataType::Int32
878 | DataType::Int64
879 | DataType::UInt8
880 | DataType::UInt16
881 | DataType::UInt32
882 | DataType::UInt64
883 | DataType::Float16
884 | DataType::Float32
885 | DataType::Float64
886 | DataType::Timestamp(_, _)
887 | DataType::Date32
888 | DataType::Date64
889 | DataType::Time32(_)
890 | DataType::Time64(_)
891 | DataType::Duration(_)
892 | DataType::Binary
893 | DataType::LargeBinary
894 | DataType::BinaryView
895 | DataType::Interval(_)
896 | DataType::LargeListView(_)
897 | DataType::ListView(_)
898 | DataType::Map(_, _)
899 | DataType::Dictionary(_, _)
900 | DataType::RunEndEncoded(_, _)
901 | DataType::FixedSizeList(_, _)
902 | DataType::FixedSizeBinary(_)
903 | DataType::Utf8
904 | DataType::LargeUtf8
905 | DataType::Utf8View
906 | DataType::Decimal32(_, _)
907 | DataType::Decimal64(_, _)
908 | DataType::Decimal128(_, _)
909 | DataType::Decimal256(_, _) => {
910 if from.data_type == DataType::Null {
911 self.nullable = true;
912 } else if self.data_type != from.data_type {
913 return Err(ArrowError::SchemaError(format!(
914 "Fail to merge schema field '{}' because the from data_type = {} does not equal {}",
915 self.name, from.data_type, self.data_type
916 )));
917 }
918 }
919 }
920 self.nullable |= from.nullable;
921
922 Ok(())
923 }
924
925 pub fn contains(&self, other: &Field) -> bool {
931 self.name == other.name
932 && self.data_type.contains(&other.data_type)
933 && self.dict_is_ordered == other.dict_is_ordered
934 && (self.nullable || !other.nullable)
936 && other.metadata.iter().all(|(k, v1)| {
938 self.metadata.get(k).is_some_and(|v2| v1 == v2)
939 })
940 }
941
942 pub fn size(&self) -> usize {
946 std::mem::size_of_val(self) - std::mem::size_of_val(&self.data_type)
947 + self.data_type.size()
948 + self.name.capacity()
949 + (std::mem::size_of::<(String, String)>() * self.metadata.len())
950 + self
951 .metadata
952 .iter()
953 .map(|(k, v)| k.capacity() + v.capacity())
954 .sum::<usize>()
955 }
956}
957
958impl std::fmt::Display for Field {
959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
960 #![expect(deprecated)] let Self {
962 name,
963 data_type,
964 nullable,
965 dict_id,
966 dict_is_ordered,
967 metadata,
968 } = self;
969 let maybe_nullable = if *nullable { "nullable " } else { "" };
970 let metadata_str = if metadata.is_empty() {
971 String::new()
972 } else {
973 format!(", metadata: {metadata:?}")
974 };
975 let dict_id_str = if dict_id == &0 {
976 String::new()
977 } else {
978 format!(", dict_id: {dict_id}")
979 };
980 let dict_is_ordered_str = if *dict_is_ordered {
981 ", dict_is_ordered"
982 } else {
983 ""
984 };
985 write!(
986 f,
987 "Field {{ {name:?}: {maybe_nullable}{data_type}{dict_id_str}{dict_is_ordered_str}{metadata_str} }}"
988 )
989 }
990}
991
992#[cfg(test)]
993mod test {
994 use super::*;
995 use std::collections::HashMap;
996 use std::collections::hash_map::DefaultHasher;
997
998 #[derive(Debug, Clone, Copy)]
999 struct TestExtensionType;
1000
1001 impl ExtensionType for TestExtensionType {
1002 const NAME: &'static str = "test.extension";
1003 type Metadata = ();
1004
1005 fn metadata(&self) -> &Self::Metadata {
1006 &()
1007 }
1008
1009 fn serialize_metadata(&self) -> Option<String> {
1010 None
1011 }
1012
1013 fn deserialize_metadata(metadata: Option<&str>) -> Result<Self::Metadata, ArrowError> {
1014 metadata.map_or(Ok(()), |_| {
1015 Err(ArrowError::InvalidArgumentError(
1016 "TestExtensionType expects no metadata".to_owned(),
1017 ))
1018 })
1019 }
1020
1021 fn supports_data_type(&self, _data_type: &DataType) -> Result<(), ArrowError> {
1022 Ok(())
1023 }
1024
1025 fn try_new(_data_type: &DataType, _metadata: Self::Metadata) -> Result<Self, ArrowError> {
1026 Ok(Self)
1027 }
1028 }
1029
1030 #[test]
1031 fn test_has_valid_extension_type() {
1032 let no_extension = Field::new("f", DataType::Null, false);
1033 assert!(!no_extension.has_valid_extension_type::<TestExtensionType>());
1034
1035 let matching_name = Field::new("f", DataType::Null, false)
1036 .with_metadata([(EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME)]);
1037 assert!(matching_name.has_valid_extension_type::<TestExtensionType>());
1038
1039 let matching_name_with_invalid_metadata = Field::new("f", DataType::Null, false)
1040 .with_metadata([
1041 (EXTENSION_TYPE_NAME_KEY, TestExtensionType::NAME),
1042 (EXTENSION_TYPE_METADATA_KEY, "invalid"),
1043 ]);
1044 assert!(
1045 !matching_name_with_invalid_metadata.has_valid_extension_type::<TestExtensionType>()
1046 );
1047
1048 let different_name = Field::new("f", DataType::Null, false)
1049 .with_metadata([(EXTENSION_TYPE_NAME_KEY, "some.other_extension")]);
1050 assert!(!different_name.has_valid_extension_type::<TestExtensionType>());
1051 }
1052
1053 #[test]
1054 fn test_new_with_string() {
1055 let s = "c1";
1057 Field::new(s, DataType::Int64, false);
1058 }
1059
1060 #[test]
1061 fn test_new_dict_with_string() {
1062 let s = "c1";
1064 #[expect(deprecated)]
1065 Field::new_dict(s, DataType::Int64, false, 4, false);
1066 }
1067
1068 #[test]
1069 #[cfg_attr(miri, ignore)] fn test_debug_format_field() {
1071 insta::assert_debug_snapshot!(Field::new("item", DataType::UInt8, false), @r"
1073 Field {
1074 data_type: UInt8,
1075 }
1076 ");
1077 insta::assert_debug_snapshot!(Field::new("column", DataType::LargeUtf8, true), @r#"
1078 Field {
1079 name: "column",
1080 data_type: LargeUtf8,
1081 nullable: true,
1082 }
1083 "#);
1084 }
1085
1086 #[test]
1087 fn test_merge_incompatible_types() {
1088 let mut field = Field::new("c1", DataType::Int64, false);
1089 let result = field
1090 .try_merge(&Field::new("c1", DataType::Float32, true))
1091 .expect_err("should fail")
1092 .to_string();
1093 assert_eq!(
1094 "Schema error: Fail to merge schema field 'c1' because the from data_type = Float32 does not equal Int64",
1095 result
1096 );
1097 }
1098
1099 #[test]
1100 fn test_merge_with_null() {
1101 let mut field1 = Field::new("c1", DataType::Null, true);
1102 field1
1103 .try_merge(&Field::new("c1", DataType::Float32, false))
1104 .expect("should widen type to nullable float");
1105 assert_eq!(Field::new("c1", DataType::Float32, true), field1);
1106
1107 let mut field2 = Field::new("c2", DataType::Utf8, false);
1108 field2
1109 .try_merge(&Field::new("c2", DataType::Null, true))
1110 .expect("should widen type to nullable utf8");
1111 assert_eq!(Field::new("c2", DataType::Utf8, true), field2);
1112 }
1113
1114 #[test]
1115 fn test_merge_with_nested_null() {
1116 let mut struct1 = Field::new(
1117 "s1",
1118 DataType::Struct(Fields::from(vec![Field::new(
1119 "inner",
1120 DataType::Float32,
1121 false,
1122 )])),
1123 false,
1124 );
1125
1126 let struct2 = Field::new(
1127 "s2",
1128 DataType::Struct(Fields::from(vec![Field::new(
1129 "inner",
1130 DataType::Null,
1131 false,
1132 )])),
1133 true,
1134 );
1135
1136 struct1
1137 .try_merge(&struct2)
1138 .expect("should widen inner field's type to nullable float");
1139 assert_eq!(
1140 Field::new(
1141 "s1",
1142 DataType::Struct(Fields::from(vec![Field::new(
1143 "inner",
1144 DataType::Float32,
1145 true,
1146 )])),
1147 true,
1148 ),
1149 struct1
1150 );
1151
1152 let mut list1 = Field::new(
1153 "l1",
1154 DataType::List(Field::new("inner", DataType::Float32, false).into()),
1155 false,
1156 );
1157
1158 let list2 = Field::new(
1159 "l2",
1160 DataType::List(Field::new("inner", DataType::Null, false).into()),
1161 true,
1162 );
1163
1164 list1
1165 .try_merge(&list2)
1166 .expect("should widen inner field's type to nullable float");
1167 assert_eq!(
1168 Field::new(
1169 "l1",
1170 DataType::List(Field::new("inner", DataType::Float32, true).into()),
1171 true,
1172 ),
1173 list1
1174 );
1175
1176 let mut large_list1 = Field::new(
1177 "ll1",
1178 DataType::LargeList(Field::new("inner", DataType::Float32, false).into()),
1179 false,
1180 );
1181
1182 let large_list2 = Field::new(
1183 "ll2",
1184 DataType::LargeList(Field::new("inner", DataType::Null, false).into()),
1185 true,
1186 );
1187
1188 large_list1
1189 .try_merge(&large_list2)
1190 .expect("should widen inner field's type to nullable float");
1191 assert_eq!(
1192 Field::new(
1193 "ll1",
1194 DataType::LargeList(Field::new("inner", DataType::Float32, true).into()),
1195 true,
1196 ),
1197 large_list1
1198 );
1199 }
1200
1201 #[test]
1202 fn test_fields_with_dict_id() {
1203 #[expect(deprecated)]
1204 let dict1 = Field::new_dict(
1205 "dict1",
1206 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1207 false,
1208 10,
1209 false,
1210 );
1211 #[expect(deprecated)]
1212 let dict2 = Field::new_dict(
1213 "dict2",
1214 DataType::Dictionary(DataType::Int32.into(), DataType::Int8.into()),
1215 false,
1216 20,
1217 false,
1218 );
1219
1220 let field = Field::new(
1221 "struct<dict1, list[struct<dict2, list[struct<dict1]>]>",
1222 DataType::Struct(Fields::from(vec![
1223 dict1.clone(),
1224 Field::new(
1225 "list[struct<dict1, list[struct<dict2>]>]",
1226 DataType::List(Arc::new(Field::new(
1227 "struct<dict1, list[struct<dict2>]>",
1228 DataType::Struct(Fields::from(vec![
1229 dict1.clone(),
1230 Field::new(
1231 "list[struct<dict2>]",
1232 DataType::List(Arc::new(Field::new(
1233 "struct<dict2>",
1234 DataType::Struct(vec![dict2.clone()].into()),
1235 false,
1236 ))),
1237 false,
1238 ),
1239 ])),
1240 false,
1241 ))),
1242 false,
1243 ),
1244 ])),
1245 false,
1246 );
1247
1248 #[expect(deprecated)]
1249 for field in field.fields_with_dict_id(10) {
1250 assert_eq!(dict1, *field);
1251 }
1252 #[expect(deprecated)]
1253 for field in field.fields_with_dict_id(20) {
1254 assert_eq!(dict2, *field);
1255 }
1256 }
1257
1258 fn get_field_hash(field: &Field) -> u64 {
1259 let mut s = DefaultHasher::new();
1260 field.hash(&mut s);
1261 s.finish()
1262 }
1263
1264 #[test]
1265 fn test_field_comparison_case() {
1266 #[expect(deprecated)]
1268 let dict1 = Field::new_dict(
1269 "dict1",
1270 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1271 false,
1272 10,
1273 false,
1274 );
1275 #[expect(deprecated)]
1276 let dict2 = Field::new_dict(
1277 "dict1",
1278 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1279 false,
1280 20,
1281 false,
1282 );
1283
1284 assert_eq!(dict1, dict2);
1285 assert_eq!(get_field_hash(&dict1), get_field_hash(&dict2));
1286
1287 #[expect(deprecated)]
1288 let dict1 = Field::new_dict(
1289 "dict0",
1290 DataType::Dictionary(DataType::Utf8.into(), DataType::Int32.into()),
1291 false,
1292 10,
1293 false,
1294 );
1295
1296 assert_ne!(dict1, dict2);
1297 assert_ne!(get_field_hash(&dict1), get_field_hash(&dict2));
1298 }
1299
1300 #[test]
1301 fn test_field_comparison_metadata() {
1302 let f1 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1303 (String::from("k1"), String::from("v1")),
1304 (String::from("k2"), String::from("v2")),
1305 ]));
1306 let f2 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1307 (String::from("k1"), String::from("v1")),
1308 (String::from("k3"), String::from("v3")),
1309 ]));
1310 let f3 = Field::new("x", DataType::Binary, false).with_metadata(HashMap::from([
1311 (String::from("k1"), String::from("v1")),
1312 (String::from("k3"), String::from("v4")),
1313 ]));
1314
1315 assert!(f1.cmp(&f2).is_lt());
1316 assert!(f2.cmp(&f3).is_lt());
1317 assert!(f1.cmp(&f3).is_lt());
1318 }
1319
1320 #[test]
1321 #[expect(clippy::needless_borrows_for_generic_args)] fn test_field_as_ref() {
1323 let field = || Field::new("x", DataType::Binary, false);
1324
1325 fn accept_ref(_: impl AsRef<Field>) {}
1328
1329 accept_ref(field());
1330 accept_ref(&field());
1331 accept_ref(&&field());
1332 accept_ref(Arc::new(field()));
1333 accept_ref(&Arc::new(field()));
1334 accept_ref(&&Arc::new(field()));
1335
1336 fn accept_refs(_: impl IntoIterator<Item: AsRef<Field>>) {}
1339
1340 accept_refs(vec![field()]);
1341 accept_refs(vec![&field()]);
1342 accept_refs(vec![Arc::new(field())]);
1343 accept_refs(vec![&Arc::new(field())]);
1344 accept_refs(&vec![field()]);
1345 accept_refs(&vec![&field()]);
1346 accept_refs(&vec![Arc::new(field())]);
1347 accept_refs(&vec![&Arc::new(field())]);
1348 }
1349
1350 #[test]
1351 fn test_contains_reflexivity() {
1352 let mut field = Field::new("field1", DataType::Float16, false);
1353 field.set_metadata(HashMap::from([
1354 (String::from("k0"), String::from("v0")),
1355 (String::from("k1"), String::from("v1")),
1356 ]));
1357 assert!(field.contains(&field))
1358 }
1359
1360 #[test]
1361 fn test_contains_transitivity() {
1362 let child_field = Field::new("child1", DataType::Float16, false);
1363
1364 let mut field1 = Field::new(
1365 "field1",
1366 DataType::Struct(Fields::from(vec![child_field])),
1367 false,
1368 );
1369 field1.set_metadata(HashMap::from([(String::from("k1"), String::from("v1"))]));
1370
1371 let mut field2 = Field::new("field1", DataType::Struct(Fields::default()), true);
1372 field2.set_metadata(HashMap::from([(String::from("k2"), String::from("v2"))]));
1373 field2.try_merge(&field1).unwrap();
1374
1375 let mut field3 = Field::new("field1", DataType::Struct(Fields::default()), false);
1376 field3.set_metadata(HashMap::from([(String::from("k3"), String::from("v3"))]));
1377 field3.try_merge(&field2).unwrap();
1378
1379 assert!(field2.contains(&field1));
1380 assert!(field3.contains(&field2));
1381 assert!(field3.contains(&field1));
1382
1383 assert!(!field1.contains(&field2));
1384 assert!(!field1.contains(&field3));
1385 assert!(!field2.contains(&field3));
1386 }
1387
1388 #[test]
1389 fn test_contains_nullable() {
1390 let field1 = Field::new("field1", DataType::Boolean, true);
1391 let field2 = Field::new("field1", DataType::Boolean, false);
1392 assert!(field1.contains(&field2));
1393 assert!(!field2.contains(&field1));
1394 }
1395
1396 #[test]
1397 fn test_contains_must_have_same_fields() {
1398 let child_field1 = Field::new("child1", DataType::Float16, false);
1399 let child_field2 = Field::new("child2", DataType::Float16, false);
1400
1401 let field1 = Field::new(
1402 "field1",
1403 DataType::Struct(vec![child_field1.clone()].into()),
1404 true,
1405 );
1406 let field2 = Field::new(
1407 "field1",
1408 DataType::Struct(vec![child_field1, child_field2].into()),
1409 true,
1410 );
1411
1412 assert!(!field1.contains(&field2));
1413 assert!(!field2.contains(&field1));
1414
1415 let field1 = Field::new(
1417 "field1",
1418 DataType::Union(
1419 UnionFields::try_new(
1420 vec![1, 2],
1421 vec![
1422 Field::new("field1", DataType::UInt8, true),
1423 Field::new("field3", DataType::Utf8, false),
1424 ],
1425 )
1426 .unwrap(),
1427 UnionMode::Dense,
1428 ),
1429 true,
1430 );
1431 let field2 = Field::new(
1432 "field1",
1433 DataType::Union(
1434 UnionFields::try_new(
1435 vec![1, 3],
1436 vec![
1437 Field::new("field1", DataType::UInt8, false),
1438 Field::new("field3", DataType::Utf8, false),
1439 ],
1440 )
1441 .unwrap(),
1442 UnionMode::Dense,
1443 ),
1444 true,
1445 );
1446 assert!(!field1.contains(&field2));
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, 2],
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
1482 #[cfg(feature = "serde")]
1483 fn assert_binary_serde_round_trip(field: Field) {
1484 let serialized = postcard::to_stdvec(&field).unwrap();
1485 let deserialized: Field = postcard::from_bytes(&serialized).unwrap();
1486 assert_eq!(field, deserialized)
1487 }
1488
1489 #[cfg(feature = "serde")]
1490 #[test]
1491 fn test_field_without_metadata_serde() {
1492 let field = Field::new("name", DataType::Boolean, true);
1493 assert_binary_serde_round_trip(field)
1494 }
1495
1496 #[cfg(feature = "serde")]
1497 #[test]
1498 fn test_field_with_empty_metadata_serde() {
1499 let field = Field::new("name", DataType::Boolean, false).with_metadata(HashMap::new());
1500
1501 assert_binary_serde_round_trip(field)
1502 }
1503
1504 #[cfg(feature = "serde")]
1505 #[test]
1506 fn test_field_with_nonempty_metadata_serde() {
1507 let mut metadata = HashMap::new();
1508 metadata.insert("hi".to_owned(), String::new());
1509 let field = Field::new("name", DataType::Boolean, false).with_metadata(metadata);
1510
1511 assert_binary_serde_round_trip(field)
1512 }
1513
1514 #[test]
1515 fn test_merge_compound_with_null() {
1516 let mut field = Field::new(
1518 "s",
1519 DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int32, false)])),
1520 false,
1521 );
1522 field
1523 .try_merge(&Field::new("s", DataType::Null, true))
1524 .expect("Struct should merge with Null");
1525 assert!(field.is_nullable());
1526 assert!(matches!(field.data_type(), DataType::Struct(_)));
1527
1528 let mut field = Field::new(
1530 "l",
1531 DataType::List(Field::new("item", DataType::Utf8, false).into()),
1532 false,
1533 );
1534 field
1535 .try_merge(&Field::new("l", DataType::Null, true))
1536 .expect("List should merge with Null");
1537 assert!(field.is_nullable());
1538 assert!(matches!(field.data_type(), DataType::List(_)));
1539
1540 let mut field = Field::new(
1542 "ll",
1543 DataType::LargeList(Field::new("item", DataType::Utf8, false).into()),
1544 false,
1545 );
1546 field
1547 .try_merge(&Field::new("ll", DataType::Null, true))
1548 .expect("LargeList should merge with Null");
1549 assert!(field.is_nullable());
1550 assert!(matches!(field.data_type(), DataType::LargeList(_)));
1551
1552 let mut field = Field::new(
1554 "u",
1555 DataType::Union(
1556 UnionFields::try_new(vec![0], vec![Field::new("f", DataType::Int32, false)])
1557 .unwrap(),
1558 UnionMode::Dense,
1559 ),
1560 false,
1561 );
1562 field
1563 .try_merge(&Field::new("u", DataType::Null, true))
1564 .expect("Union should merge with Null");
1565 assert!(matches!(field.data_type(), DataType::Union(_, _)));
1566 }
1567}