1use crate::{
38 ArrowError, DataType, Field, FieldRef, IntervalUnit, Schema, TimeUnit, UnionFields, UnionMode,
39};
40use bitflags::bitflags;
41use std::borrow::Cow;
42use std::sync::Arc;
43use std::{
44 collections::HashMap,
45 ffi::{CStr, CString, c_char, c_void},
46};
47
48bitflags! {
49 pub struct Flags: i64 {
54 const DICTIONARY_ORDERED = 0b00000001;
56 const NULLABLE = 0b00000010;
58 const MAP_KEYS_SORTED = 0b00000100;
60 }
61}
62
63#[repr(C)]
75#[derive(Debug)]
76pub struct FFI_ArrowSchema {
77 format: *const c_char,
81 name: *const c_char,
83 metadata: *const c_char,
85 flags: i64,
88 n_children: i64,
90 children: *mut *mut FFI_ArrowSchema,
92 dictionary: *mut FFI_ArrowSchema,
94 release: Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowSchema)>,
96 private_data: *mut c_void,
98}
99
100struct SchemaPrivateData {
101 children: Box<[*mut FFI_ArrowSchema]>,
102 dictionary: *mut FFI_ArrowSchema,
103 metadata: Option<Vec<u8>>,
104}
105
106unsafe extern "C" fn release_schema(schema: *mut FFI_ArrowSchema) {
108 if schema.is_null() {
109 return;
110 }
111 let schema = unsafe { &mut *schema };
112
113 drop(unsafe { CString::from_raw(schema.format.cast_mut()) });
115 if !schema.name.is_null() {
116 drop(unsafe { CString::from_raw(schema.name.cast_mut()) });
117 }
118 if !schema.private_data.is_null() {
119 let private_data =
120 unsafe { Box::from_raw(schema.private_data.cast::<SchemaPrivateData>()) };
121 for child in &private_data.children {
122 drop(unsafe { Box::from_raw(*child) })
123 }
124 if !private_data.dictionary.is_null() {
125 drop(unsafe { Box::from_raw(private_data.dictionary) });
126 }
127
128 drop(private_data);
129 }
130
131 schema.release = None;
132}
133
134impl FFI_ArrowSchema {
135 pub fn try_new(
142 format: &str,
143 children: Vec<FFI_ArrowSchema>,
144 dictionary: Option<FFI_ArrowSchema>,
145 ) -> Result<Self, ArrowError> {
146 let format = CString::new(format).map_err(|err| {
149 ArrowError::CDataInterface(format!(
150 "Null byte at position {} not allowed in format",
151 err.nul_position()
152 ))
153 })?;
154
155 let mut this = Self::empty();
156
157 let children_ptr = children
158 .into_iter()
159 .map(Box::new)
160 .map(Box::into_raw)
161 .collect::<Box<_>>();
162
163 this.format = format.into_raw();
164 this.release = Some(release_schema);
165 this.n_children = children_ptr.len() as i64;
166
167 let dictionary_ptr = dictionary
168 .map(|d| Box::into_raw(Box::new(d)))
169 .unwrap_or(std::ptr::null_mut());
170
171 let mut private_data = Box::new(SchemaPrivateData {
172 children: children_ptr,
173 dictionary: dictionary_ptr,
174 metadata: None,
175 });
176
177 this.children = private_data.children.as_mut_ptr();
179
180 this.dictionary = dictionary_ptr;
181
182 this.private_data = Box::into_raw(private_data).cast::<c_void>();
183
184 Ok(this)
185 }
186
187 pub fn with_name(mut self, name: &str) -> Result<Self, ArrowError> {
189 self.name = CString::new(name)
190 .map_err(|e| {
191 ArrowError::CDataInterface(format!(
192 "Null byte at position {} not allowed in name",
193 e.nul_position()
194 ))
195 })?
196 .into_raw();
197 Ok(self)
198 }
199
200 pub fn with_flags(mut self, flags: Flags) -> Result<Self, ArrowError> {
202 self.flags = flags.bits();
203 Ok(self)
204 }
205
206 pub unsafe fn with_metadata<I, S>(mut self, metadata: I) -> Result<Self, ArrowError>
217 where
218 I: IntoIterator<Item = (S, S)>,
219 S: AsRef<str>,
220 {
221 let metadata: Vec<(S, S)> = metadata.into_iter().collect();
222 let new_metadata = if !metadata.is_empty() {
224 let mut metadata_serialized: Vec<u8> = Vec::new();
225 let num_entries: i32 = metadata.len().try_into().map_err(|_| {
226 ArrowError::CDataInterface(format!(
227 "metadata can only have {} entries, but {} were provided",
228 i32::MAX,
229 metadata.len()
230 ))
231 })?;
232 metadata_serialized.extend(num_entries.to_ne_bytes());
233
234 for (key, value) in metadata {
235 let key_len: i32 = key.as_ref().len().try_into().map_err(|_| {
236 ArrowError::CDataInterface(format!(
237 "metadata key can only have {} bytes, but {} were provided",
238 i32::MAX,
239 key.as_ref().len()
240 ))
241 })?;
242 let value_len: i32 = value.as_ref().len().try_into().map_err(|_| {
243 ArrowError::CDataInterface(format!(
244 "metadata value can only have {} bytes, but {} were provided",
245 i32::MAX,
246 value.as_ref().len()
247 ))
248 })?;
249
250 metadata_serialized.extend(key_len.to_ne_bytes());
251 metadata_serialized.extend_from_slice(key.as_ref().as_bytes());
252 metadata_serialized.extend(value_len.to_ne_bytes());
253 metadata_serialized.extend_from_slice(value.as_ref().as_bytes());
254 }
255
256 self.metadata = metadata_serialized.as_ptr().cast::<c_char>();
257 Some(metadata_serialized)
258 } else {
259 self.metadata = std::ptr::null_mut();
260 None
261 };
262
263 unsafe {
267 let mut private_data = Box::from_raw(self.private_data.cast::<SchemaPrivateData>());
268 private_data.metadata = new_metadata;
269 self.private_data = Box::into_raw(private_data).cast::<c_void>();
270 }
271
272 Ok(self)
273 }
274
275 pub unsafe fn from_raw(schema: *mut FFI_ArrowSchema) -> Self {
288 unsafe { std::ptr::replace(schema, Self::empty()) }
289 }
290
291 pub fn empty() -> Self {
293 Self {
294 format: std::ptr::null_mut(),
295 name: std::ptr::null_mut(),
296 metadata: std::ptr::null_mut(),
297 flags: 0,
298 n_children: 0,
299 children: std::ptr::null_mut(),
300 dictionary: std::ptr::null_mut(),
301 release: None,
302 private_data: std::ptr::null_mut(),
303 }
304 }
305
306 pub fn release(&self) -> Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowSchema)> {
308 self.release
309 }
310
311 pub fn private_data(&self) -> *mut c_void {
313 self.private_data
314 }
315
316 pub unsafe fn set_release(
328 &mut self,
329 release: Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowSchema)>,
330 ) -> Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowSchema)> {
331 std::mem::replace(&mut self.release, release)
332 }
333
334 pub unsafe fn set_private_data(&mut self, private_data: *mut c_void) -> *mut c_void {
342 std::mem::replace(&mut self.private_data, private_data)
343 }
344
345 pub fn format(&self) -> &str {
351 assert!(!self.format.is_null());
352 unsafe { CStr::from_ptr(self.format) }
354 .to_str()
355 .expect("The external API has a non-utf8 as format")
356 }
357
358 pub fn name(&self) -> Option<&str> {
364 if self.name.is_null() {
365 None
366 } else {
367 Some(
369 unsafe { CStr::from_ptr(self.name) }
370 .to_str()
371 .expect("The external API has a non-utf8 as name"),
372 )
373 }
374 }
375
376 pub fn flags(&self) -> Option<Flags> {
378 Flags::from_bits(self.flags)
379 }
380
381 pub fn child(&self, index: usize) -> &Self {
389 assert!(index < self.n_children as usize);
390 unsafe { self.children.add(index).as_ref().unwrap().as_ref().unwrap() }
391 }
392
393 pub fn children(&self) -> impl Iterator<Item = &Self> {
395 (0..self.n_children as usize).map(move |i| self.child(i))
396 }
397
398 pub fn nullable(&self) -> bool {
401 (self.flags / 2) & 1 == 1
402 }
403
404 pub fn dictionary(&self) -> Option<&Self> {
409 unsafe { self.dictionary.as_ref() }
412 }
413
414 pub fn map_keys_sorted(&self) -> bool {
418 self.flags & 0b00000100 != 0
419 }
420
421 pub fn dictionary_ordered(&self) -> bool {
423 self.flags & 0b00000001 != 0
424 }
425
426 pub fn metadata(&self) -> Result<HashMap<String, String>, ArrowError> {
428 if self.metadata.is_null() {
429 Ok(HashMap::new())
430 } else {
431 let mut pos = 0;
432
433 let buffer = self.metadata.cast::<u8>();
435
436 fn next_four_bytes(buffer: *const u8, pos: &mut usize) -> [u8; 4] {
437 let out = unsafe {
440 [
441 *buffer.add(*pos),
442 *buffer.add(*pos + 1),
443 *buffer.add(*pos + 2),
444 *buffer.add(*pos + 3),
445 ]
446 };
447 *pos += 4;
448 out
449 }
450
451 fn next_n_bytes(buffer: *const u8, pos: &mut usize, n: usize) -> &[u8] {
452 let out = unsafe { std::slice::from_raw_parts(buffer.add(*pos), n) };
454 *pos += n;
455 out
456 }
457
458 fn checked_length(what: &str, length: i32) -> Result<usize, ArrowError> {
460 usize::try_from(length).map_err(|_| {
461 ArrowError::CDataInterface(format!("Invalid {what} in metadata: {length}"))
462 })
463 }
464
465 let num_entries = i32::from_ne_bytes(next_four_bytes(buffer, &mut pos));
466 let num_entries = checked_length("number of entries", num_entries)?;
467
468 let mut metadata = HashMap::with_capacity(num_entries.min(128));
470
471 for _ in 0..num_entries {
472 let key_length = i32::from_ne_bytes(next_four_bytes(buffer, &mut pos));
473 let key_length = checked_length("key length", key_length)?;
474 let key = String::from_utf8(next_n_bytes(buffer, &mut pos, key_length).to_vec())?;
475 let value_length = i32::from_ne_bytes(next_four_bytes(buffer, &mut pos));
476 let value_length = checked_length("value length", value_length)?;
477 let value =
478 String::from_utf8(next_n_bytes(buffer, &mut pos, value_length).to_vec())?;
479 metadata.insert(key, value);
480 }
481
482 Ok(metadata)
483 }
484 }
485}
486
487impl Drop for FFI_ArrowSchema {
488 fn drop(&mut self) {
489 match self.release {
490 None => (),
491 Some(release) => unsafe { release(self) },
495 }
496 }
497}
498
499unsafe impl Send for FFI_ArrowSchema {}
500
501impl TryFrom<&FFI_ArrowSchema> for DataType {
502 type Error = ArrowError;
503
504 fn try_from(c_schema: &FFI_ArrowSchema) -> Result<Self, ArrowError> {
506 let mut dtype = match c_schema.format() {
507 "n" => DataType::Null,
508 "b" => DataType::Boolean,
509 "c" => DataType::Int8,
510 "C" => DataType::UInt8,
511 "s" => DataType::Int16,
512 "S" => DataType::UInt16,
513 "i" => DataType::Int32,
514 "I" => DataType::UInt32,
515 "l" => DataType::Int64,
516 "L" => DataType::UInt64,
517 "e" => DataType::Float16,
518 "f" => DataType::Float32,
519 "g" => DataType::Float64,
520 "vz" => DataType::BinaryView,
521 "z" => DataType::Binary,
522 "Z" => DataType::LargeBinary,
523 "vu" => DataType::Utf8View,
524 "u" => DataType::Utf8,
525 "U" => DataType::LargeUtf8,
526 "tdD" => DataType::Date32,
527 "tdm" => DataType::Date64,
528 "tts" => DataType::Time32(TimeUnit::Second),
529 "ttm" => DataType::Time32(TimeUnit::Millisecond),
530 "ttu" => DataType::Time64(TimeUnit::Microsecond),
531 "ttn" => DataType::Time64(TimeUnit::Nanosecond),
532 "tDs" => DataType::Duration(TimeUnit::Second),
533 "tDm" => DataType::Duration(TimeUnit::Millisecond),
534 "tDu" => DataType::Duration(TimeUnit::Microsecond),
535 "tDn" => DataType::Duration(TimeUnit::Nanosecond),
536 "tiM" => DataType::Interval(IntervalUnit::YearMonth),
537 "tiD" => DataType::Interval(IntervalUnit::DayTime),
538 "tin" => DataType::Interval(IntervalUnit::MonthDayNano),
539 "+l" => {
540 let c_child = c_schema.child(0);
541 DataType::List(Arc::new(Field::try_from(c_child)?))
542 }
543 "+L" => {
544 let c_child = c_schema.child(0);
545 DataType::LargeList(Arc::new(Field::try_from(c_child)?))
546 }
547 "+vl" => {
548 let c_child = c_schema.child(0);
549 DataType::ListView(Arc::new(Field::try_from(c_child)?))
550 }
551 "+vL" => {
552 let c_child = c_schema.child(0);
553 DataType::LargeListView(Arc::new(Field::try_from(c_child)?))
554 }
555 "+s" => {
556 let fields = c_schema.children().map(Field::try_from);
557 DataType::Struct(fields.collect::<Result<_, ArrowError>>()?)
558 }
559 "+m" => {
560 let c_child = c_schema.child(0);
561 let map_keys_sorted = c_schema.map_keys_sorted();
562 DataType::Map(Arc::new(Field::try_from(c_child)?), map_keys_sorted)
563 }
564 "+r" => {
565 let c_run_ends = c_schema.child(0);
566 let c_values = c_schema.child(1);
567 DataType::RunEndEncoded(
568 Arc::new(Field::try_from(c_run_ends)?),
569 Arc::new(Field::try_from(c_values)?),
570 )
571 }
572 other => {
574 match other.splitn(2, ':').collect::<Vec<&str>>().as_slice() {
575 ["w", num_bytes] => {
577 let parsed_num_bytes = num_bytes.parse::<i32>().map_err(|_| {
578 ArrowError::CDataInterface(
579 "FixedSizeBinary requires an integer parameter representing number of bytes per element".to_string())
580 })?;
581 DataType::FixedSizeBinary(parsed_num_bytes)
582 }
583 ["+w", num_elems] => {
585 let c_child = c_schema.child(0);
586 let parsed_num_elems = num_elems.parse::<i32>().map_err(|_| {
587 ArrowError::CDataInterface(
588 "The FixedSizeList type requires an integer parameter representing number of elements per list".to_string())
589 })?;
590 DataType::FixedSizeList(
591 Arc::new(Field::try_from(c_child)?),
592 parsed_num_elems,
593 )
594 }
595 ["d", extra] => match extra.splitn(3, ',').collect::<Vec<&str>>().as_slice() {
597 [precision, scale] => {
598 let parsed_precision = precision.parse::<u8>().map_err(|_| {
599 ArrowError::CDataInterface(
600 "The decimal type requires an integer precision".to_string(),
601 )
602 })?;
603 let parsed_scale = scale.parse::<i8>().map_err(|_| {
604 ArrowError::CDataInterface(
605 "The decimal type requires an integer scale".to_string(),
606 )
607 })?;
608 DataType::Decimal128(parsed_precision, parsed_scale)
609 }
610 [precision, scale, bits] => {
611 let parsed_precision = precision.parse::<u8>().map_err(|_| {
612 ArrowError::CDataInterface(
613 "The decimal type requires an integer precision".to_string(),
614 )
615 })?;
616 let parsed_scale = scale.parse::<i8>().map_err(|_| {
617 ArrowError::CDataInterface(
618 "The decimal type requires an integer scale".to_string(),
619 )
620 })?;
621 match *bits {
622 "32" => DataType::Decimal32(parsed_precision, parsed_scale),
623 "64" => DataType::Decimal64(parsed_precision, parsed_scale),
624 "128" => DataType::Decimal128(parsed_precision, parsed_scale),
625 "256" => DataType::Decimal256(parsed_precision, parsed_scale),
626 _ => return Err(ArrowError::CDataInterface("Only 32/64/128/256 bit wide decimals are supported in the Rust implementation".to_string())),
627 }
628 }
629 _ => {
630 return Err(ArrowError::CDataInterface(format!(
631 "The decimal pattern \"d:{extra:?}\" is not supported in the Rust implementation"
632 )));
633 }
634 },
635 ["+ud", extra] => {
637 let type_ids = extra
638 .split(',')
639 .map(|t| {
640 t.parse::<i8>().map_err(|_| {
641 ArrowError::CDataInterface(
642 "The Union type requires an integer type id".to_string(),
643 )
644 })
645 })
646 .collect::<Result<Vec<_>, ArrowError>>()?;
647 let mut fields = Vec::with_capacity(type_ids.len());
648 for idx in 0..c_schema.n_children {
649 let c_child = c_schema.child(idx as usize);
650 let field = Field::try_from(c_child)?;
651 fields.push(field);
652 }
653
654 if fields.len() != type_ids.len() {
655 return Err(ArrowError::CDataInterface(
656 "The Union type requires same number of fields and type ids"
657 .to_string(),
658 ));
659 }
660
661 DataType::Union(UnionFields::try_new(type_ids, fields)?, UnionMode::Dense)
662 }
663 ["+us", extra] => {
665 let type_ids = extra
666 .split(',')
667 .map(|t| {
668 t.parse::<i8>().map_err(|_| {
669 ArrowError::CDataInterface(
670 "The Union type requires an integer type id".to_string(),
671 )
672 })
673 })
674 .collect::<Result<Vec<_>, ArrowError>>()?;
675 let mut fields = Vec::with_capacity(type_ids.len());
676 for idx in 0..c_schema.n_children {
677 let c_child = c_schema.child(idx as usize);
678 let field = Field::try_from(c_child)?;
679 fields.push(field);
680 }
681
682 if fields.len() != type_ids.len() {
683 return Err(ArrowError::CDataInterface(
684 "The Union type requires same number of fields and type ids"
685 .to_string(),
686 ));
687 }
688
689 DataType::Union(UnionFields::try_new(type_ids, fields)?, UnionMode::Sparse)
690 }
691
692 ["tss", ""] => DataType::Timestamp(TimeUnit::Second, None),
694 ["tsm", ""] => DataType::Timestamp(TimeUnit::Millisecond, None),
695 ["tsu", ""] => DataType::Timestamp(TimeUnit::Microsecond, None),
696 ["tsn", ""] => DataType::Timestamp(TimeUnit::Nanosecond, None),
697 ["tss", tz] => DataType::Timestamp(TimeUnit::Second, Some(Arc::from(*tz))),
698 ["tsm", tz] => DataType::Timestamp(TimeUnit::Millisecond, Some(Arc::from(*tz))),
699 ["tsu", tz] => DataType::Timestamp(TimeUnit::Microsecond, Some(Arc::from(*tz))),
700 ["tsn", tz] => DataType::Timestamp(TimeUnit::Nanosecond, Some(Arc::from(*tz))),
701 _ => {
702 return Err(ArrowError::CDataInterface(format!(
703 "The datatype \"{other:?}\" is still not supported in Rust implementation"
704 )));
705 }
706 }
707 }
708 };
709
710 if let Some(dict_schema) = c_schema.dictionary() {
711 let value_type = Self::try_from(dict_schema)?;
712 dtype = DataType::Dictionary(Box::new(dtype), Box::new(value_type));
713 }
714
715 Ok(dtype)
716 }
717}
718
719impl TryFrom<&FFI_ArrowSchema> for Field {
720 type Error = ArrowError;
721
722 fn try_from(c_schema: &FFI_ArrowSchema) -> Result<Self, ArrowError> {
723 let dtype = DataType::try_from(c_schema)?;
724 let field = Field::new(c_schema.name().unwrap_or(""), dtype, c_schema.nullable())
725 .with_dict_is_ordered(c_schema.dictionary_ordered())
726 .with_metadata(c_schema.metadata()?);
727 Ok(field)
728 }
729}
730
731impl TryFrom<&FFI_ArrowSchema> for Schema {
732 type Error = ArrowError;
733
734 fn try_from(c_schema: &FFI_ArrowSchema) -> Result<Self, ArrowError> {
735 let dtype = DataType::try_from(c_schema)?;
737 if let DataType::Struct(fields) = dtype {
738 Ok(Schema::new(fields).with_metadata(c_schema.metadata()?))
739 } else {
740 Err(ArrowError::CDataInterface(
741 "Unable to interpret C data struct as a Schema".to_string(),
742 ))
743 }
744 }
745}
746
747impl TryFrom<&DataType> for FFI_ArrowSchema {
748 type Error = ArrowError;
749
750 fn try_from(dtype: &DataType) -> Result<Self, ArrowError> {
752 let format = get_format_string(dtype)?;
753 let children = match dtype {
755 DataType::List(child)
756 | DataType::LargeList(child)
757 | DataType::ListView(child)
758 | DataType::LargeListView(child)
759 | DataType::FixedSizeList(child, _)
760 | DataType::Map(child, _) => {
761 vec![FFI_ArrowSchema::try_from(child.as_ref())?]
762 }
763 DataType::Union(fields, _) => fields
764 .iter()
765 .map(|(_, f)| f.as_ref().try_into())
766 .collect::<Result<Vec<_>, ArrowError>>()?,
767 DataType::Struct(fields) => fields
768 .iter()
769 .map(FFI_ArrowSchema::try_from)
770 .collect::<Result<Vec<_>, ArrowError>>()?,
771 DataType::RunEndEncoded(run_ends, values) => vec![
772 FFI_ArrowSchema::try_from(run_ends.as_ref())?,
773 FFI_ArrowSchema::try_from(values.as_ref())?,
774 ],
775 _ => vec![],
776 };
777 let dictionary = if let DataType::Dictionary(_, value_data_type) = dtype {
778 Some(Self::try_from(value_data_type.as_ref())?)
779 } else {
780 None
781 };
782
783 let flags = match dtype {
784 DataType::Map(_, true) => Flags::MAP_KEYS_SORTED,
785 _ => Flags::empty(),
786 };
787
788 FFI_ArrowSchema::try_new(&format, children, dictionary)?.with_flags(flags)
789 }
790}
791
792fn get_format_string(dtype: &DataType) -> Result<Cow<'static, str>, ArrowError> {
793 match dtype {
794 DataType::Null => Ok("n".into()),
795 DataType::Boolean => Ok("b".into()),
796 DataType::Int8 => Ok("c".into()),
797 DataType::UInt8 => Ok("C".into()),
798 DataType::Int16 => Ok("s".into()),
799 DataType::UInt16 => Ok("S".into()),
800 DataType::Int32 => Ok("i".into()),
801 DataType::UInt32 => Ok("I".into()),
802 DataType::Int64 => Ok("l".into()),
803 DataType::UInt64 => Ok("L".into()),
804 DataType::Float16 => Ok("e".into()),
805 DataType::Float32 => Ok("f".into()),
806 DataType::Float64 => Ok("g".into()),
807 DataType::BinaryView => Ok("vz".into()),
808 DataType::Binary => Ok("z".into()),
809 DataType::LargeBinary => Ok("Z".into()),
810 DataType::Utf8View => Ok("vu".into()),
811 DataType::Utf8 => Ok("u".into()),
812 DataType::LargeUtf8 => Ok("U".into()),
813 DataType::FixedSizeBinary(num_bytes) => Ok(Cow::Owned(format!("w:{num_bytes}"))),
814 DataType::FixedSizeList(_, num_elems) => Ok(Cow::Owned(format!("+w:{num_elems}"))),
815 DataType::Decimal32(precision, scale) => {
816 Ok(Cow::Owned(format!("d:{precision},{scale},32")))
817 }
818 DataType::Decimal64(precision, scale) => {
819 Ok(Cow::Owned(format!("d:{precision},{scale},64")))
820 }
821 DataType::Decimal128(precision, scale) => Ok(Cow::Owned(format!("d:{precision},{scale}"))),
822 DataType::Decimal256(precision, scale) => {
823 Ok(Cow::Owned(format!("d:{precision},{scale},256")))
824 }
825 DataType::Date32 => Ok("tdD".into()),
826 DataType::Date64 => Ok("tdm".into()),
827 DataType::Time32(TimeUnit::Second) => Ok("tts".into()),
828 DataType::Time32(TimeUnit::Millisecond) => Ok("ttm".into()),
829 DataType::Time64(TimeUnit::Microsecond) => Ok("ttu".into()),
830 DataType::Time64(TimeUnit::Nanosecond) => Ok("ttn".into()),
831 DataType::Timestamp(TimeUnit::Second, None) => Ok("tss:".into()),
832 DataType::Timestamp(TimeUnit::Millisecond, None) => Ok("tsm:".into()),
833 DataType::Timestamp(TimeUnit::Microsecond, None) => Ok("tsu:".into()),
834 DataType::Timestamp(TimeUnit::Nanosecond, None) => Ok("tsn:".into()),
835 DataType::Timestamp(TimeUnit::Second, Some(tz)) => Ok(Cow::Owned(format!("tss:{tz}"))),
836 DataType::Timestamp(TimeUnit::Millisecond, Some(tz)) => Ok(Cow::Owned(format!("tsm:{tz}"))),
837 DataType::Timestamp(TimeUnit::Microsecond, Some(tz)) => Ok(Cow::Owned(format!("tsu:{tz}"))),
838 DataType::Timestamp(TimeUnit::Nanosecond, Some(tz)) => Ok(Cow::Owned(format!("tsn:{tz}"))),
839 DataType::Duration(TimeUnit::Second) => Ok("tDs".into()),
840 DataType::Duration(TimeUnit::Millisecond) => Ok("tDm".into()),
841 DataType::Duration(TimeUnit::Microsecond) => Ok("tDu".into()),
842 DataType::Duration(TimeUnit::Nanosecond) => Ok("tDn".into()),
843 DataType::Interval(IntervalUnit::YearMonth) => Ok("tiM".into()),
844 DataType::Interval(IntervalUnit::DayTime) => Ok("tiD".into()),
845 DataType::Interval(IntervalUnit::MonthDayNano) => Ok("tin".into()),
846 DataType::List(_) => Ok("+l".into()),
847 DataType::LargeList(_) => Ok("+L".into()),
848 DataType::ListView(_) => Ok("+vl".into()),
849 DataType::LargeListView(_) => Ok("+vL".into()),
850 DataType::Struct(_) => Ok("+s".into()),
851 DataType::Map(_, _) => Ok("+m".into()),
852 DataType::RunEndEncoded(_, _) => Ok("+r".into()),
853 DataType::Dictionary(key_data_type, _) => get_format_string(key_data_type),
854 DataType::Union(fields, mode) => {
855 let formats = fields
856 .iter()
857 .map(|(t, _)| t.to_string())
858 .collect::<Vec<_>>();
859 match mode {
860 UnionMode::Dense => Ok(Cow::Owned(format!("{}:{}", "+ud", formats.join(",")))),
861 UnionMode::Sparse => Ok(Cow::Owned(format!("{}:{}", "+us", formats.join(",")))),
862 }
863 }
864 other => Err(ArrowError::CDataInterface(format!(
865 "The datatype \"{other:?}\" is still not supported in Rust implementation"
866 ))),
867 }
868}
869
870impl TryFrom<&FieldRef> for FFI_ArrowSchema {
871 type Error = ArrowError;
872
873 fn try_from(value: &FieldRef) -> Result<Self, Self::Error> {
874 value.as_ref().try_into()
875 }
876}
877
878impl TryFrom<&Field> for FFI_ArrowSchema {
879 type Error = ArrowError;
880
881 fn try_from(field: &Field) -> Result<Self, ArrowError> {
882 let mut flags = if field.is_nullable() {
883 Flags::NULLABLE
884 } else {
885 Flags::empty()
886 };
887
888 if field.dict_is_ordered() == Some(true) {
889 flags |= Flags::DICTIONARY_ORDERED;
890 }
891
892 let schema = FFI_ArrowSchema::try_from(field.data_type())?
893 .with_name(field.name())?
894 .with_flags(flags)?;
895 unsafe { schema.with_metadata(field.metadata()) }
897 }
898}
899
900impl TryFrom<&Schema> for FFI_ArrowSchema {
901 type Error = ArrowError;
902
903 fn try_from(schema: &Schema) -> Result<Self, ArrowError> {
904 let dtype = DataType::Struct(schema.fields().clone());
905 let c_schema = FFI_ArrowSchema::try_from(&dtype)?;
906 unsafe { c_schema.with_metadata(&schema.metadata) }
908 }
909}
910
911impl TryFrom<DataType> for FFI_ArrowSchema {
912 type Error = ArrowError;
913
914 fn try_from(dtype: DataType) -> Result<Self, ArrowError> {
915 FFI_ArrowSchema::try_from(&dtype)
916 }
917}
918
919impl TryFrom<Field> for FFI_ArrowSchema {
920 type Error = ArrowError;
921
922 fn try_from(field: Field) -> Result<Self, ArrowError> {
923 FFI_ArrowSchema::try_from(&field)
924 }
925}
926
927impl TryFrom<Schema> for FFI_ArrowSchema {
928 type Error = ArrowError;
929
930 fn try_from(schema: Schema) -> Result<Self, ArrowError> {
931 FFI_ArrowSchema::try_from(&schema)
932 }
933}
934
935#[cfg(test)]
936mod tests {
937 use super::*;
938 use crate::Fields;
939 use std::sync::atomic::{AtomicBool, Ordering};
940
941 fn round_trip_type(dtype: DataType) {
942 let c_schema = FFI_ArrowSchema::try_from(&dtype).unwrap();
943 let restored = DataType::try_from(&c_schema).unwrap();
944 assert_eq!(restored, dtype);
945 }
946
947 fn round_trip_field(field: Field) {
948 let c_schema = FFI_ArrowSchema::try_from(&field).unwrap();
949 let restored = Field::try_from(&c_schema).unwrap();
950 assert_eq!(restored, field);
951 }
952
953 fn round_trip_schema(schema: Schema) {
954 let c_schema = FFI_ArrowSchema::try_from(&schema).unwrap();
955 let restored = Schema::try_from(&c_schema).unwrap();
956 assert_eq!(restored, schema);
957 }
958
959 #[test]
960 fn test_try_new_with_interior_nul_byte() {
961 let err = FFI_ArrowSchema::try_new("i\0nt", vec![], None).unwrap_err();
962 assert_eq!(
963 err.to_string(),
964 "C Data interface error: Null byte at position 1 not allowed in format"
965 );
966 }
967
968 #[test]
969 fn test_type() {
970 round_trip_type(DataType::Int64);
971 round_trip_type(DataType::UInt64);
972 round_trip_type(DataType::Float64);
973 round_trip_type(DataType::Date64);
974 round_trip_type(DataType::Time64(TimeUnit::Nanosecond));
975 round_trip_type(DataType::FixedSizeBinary(12));
976 round_trip_type(DataType::FixedSizeList(
977 Arc::new(Field::new("a", DataType::Int64, false)),
978 5,
979 ));
980 round_trip_type(DataType::Utf8);
981 round_trip_type(DataType::Utf8View);
982 round_trip_type(DataType::BinaryView);
983 round_trip_type(DataType::Binary);
984 round_trip_type(DataType::LargeBinary);
985 round_trip_type(DataType::List(Arc::new(Field::new(
986 "a",
987 DataType::Int16,
988 false,
989 ))));
990 round_trip_type(DataType::ListView(Arc::new(Field::new(
991 "a",
992 DataType::Int16,
993 false,
994 ))));
995 round_trip_type(DataType::LargeListView(Arc::new(Field::new(
996 "a",
997 DataType::Int16,
998 false,
999 ))));
1000 round_trip_type(DataType::Struct(Fields::from(vec![Field::new(
1001 "a",
1002 DataType::Utf8,
1003 true,
1004 )])));
1005 round_trip_type(DataType::RunEndEncoded(
1006 Arc::new(Field::new("run_ends", DataType::Int32, false)),
1007 Arc::new(Field::new("values", DataType::Binary, true)),
1008 ));
1009 }
1010
1011 #[test]
1012 fn test_field() {
1013 let dtype = DataType::Struct(vec![Field::new("a", DataType::Utf8, true)].into());
1014 round_trip_field(Field::new("test", dtype, true));
1015 }
1016
1017 #[test]
1018 fn test_schema() {
1019 let schema = Schema::new(vec![
1020 Field::new("name", DataType::Utf8, false),
1021 Field::new("address", DataType::Utf8, false),
1022 Field::new("priority", DataType::UInt8, false),
1023 ])
1024 .with_metadata([("hello", "world")]);
1025
1026 round_trip_schema(schema);
1027
1028 let dtype = DataType::Struct(Fields::from(vec![
1030 Field::new("a", DataType::Utf8, true),
1031 Field::new("b", DataType::Int16, false),
1032 ]));
1033 let c_schema = FFI_ArrowSchema::try_from(&dtype).unwrap();
1034 let schema = Schema::try_from(&c_schema).unwrap();
1035 assert_eq!(schema.fields().len(), 2);
1036
1037 let c_schema = FFI_ArrowSchema::try_from(&DataType::Float64).unwrap();
1039 let result = Schema::try_from(&c_schema);
1040 assert!(result.is_err());
1041 }
1042
1043 #[test]
1044 fn test_map_keys_sorted() {
1045 let keys = Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Int32, false);
1046 let values = Field::new(Field::MAP_VALUE_FIELD_DEFAULT_NAME, DataType::UInt32, false);
1047 let entry_struct = DataType::Struct(vec![keys, values].into());
1048
1049 let map_data_type = DataType::Map(
1051 Arc::new(Field::new(
1052 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
1053 entry_struct,
1054 false,
1055 )),
1056 true,
1057 );
1058
1059 let arrow_schema = FFI_ArrowSchema::try_from(map_data_type).unwrap();
1060 assert!(arrow_schema.map_keys_sorted());
1061 }
1062
1063 #[test]
1064 fn test_dictionary_ordered() {
1065 #[expect(deprecated)]
1066 let schema = Schema::new(vec![Field::new_dict(
1067 "dict",
1068 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
1069 false,
1070 0,
1071 true,
1072 )]);
1073
1074 let arrow_schema = FFI_ArrowSchema::try_from(schema).unwrap();
1075 assert!(arrow_schema.child(0).dictionary_ordered());
1076
1077 let field = Field::try_from(arrow_schema.child(0)).unwrap();
1079 assert_eq!(field.dict_is_ordered(), Some(true));
1080 }
1081
1082 #[test]
1083 fn test_set_field_metadata() {
1084 let metadata_cases: Vec<HashMap<String, String>> = vec![
1085 [].into(),
1086 [("key".to_string(), "value".to_string())].into(),
1087 [
1088 ("key".to_string(), String::new()),
1089 ("ascii123".to_string(), "你好".to_string()),
1090 (String::new(), "value".to_string()),
1091 ]
1092 .into(),
1093 ];
1094
1095 let mut schema = FFI_ArrowSchema::try_new("b", vec![], None)
1096 .unwrap()
1097 .with_name("test")
1098 .unwrap();
1099
1100 for metadata in metadata_cases {
1101 schema = unsafe { schema.with_metadata(&metadata) }.unwrap();
1103 let field = Field::try_from(&schema).unwrap();
1104 assert_eq!(field.metadata(), &metadata);
1105 }
1106 }
1107
1108 #[test]
1109 fn test_name_with_null_byte() {
1110 let schema = FFI_ArrowSchema::try_new("i", vec![], None).unwrap();
1111 assert!(schema.with_name("ab\0cd").is_err());
1112 }
1113
1114 #[test]
1115 fn test_import_field_with_null_name() {
1116 let dtype = DataType::Int16;
1117 let c_schema = FFI_ArrowSchema::try_from(&dtype).unwrap();
1118 assert!(c_schema.name().is_none());
1119 let field = Field::try_from(&c_schema).unwrap();
1120 assert_eq!(field.name(), "");
1121 }
1122
1123 static WRAPPER_RAN: AtomicBool = AtomicBool::new(false);
1127
1128 struct WrapperData {
1129 original_release: Option<unsafe extern "C" fn(*mut FFI_ArrowSchema)>,
1130 original_private_data: *mut c_void,
1131 }
1132
1133 unsafe extern "C" fn wrapping_release(schema: *mut FFI_ArrowSchema) {
1134 let schema = unsafe { &mut *schema };
1135 let data = unsafe { Box::from_raw(schema.private_data().cast::<WrapperData>()) };
1136 WRAPPER_RAN.store(true, Ordering::SeqCst);
1137 unsafe { schema.set_release(data.original_release) };
1139 unsafe { schema.set_private_data(data.original_private_data) };
1140 if let Some(release) = schema.release() {
1141 unsafe { release(schema) };
1142 }
1143 }
1144
1145 #[test]
1146 fn test_wrap_release_callback() {
1147 let mut schema = FFI_ArrowSchema::try_from(&DataType::Int32).unwrap();
1148
1149 let data = Box::new(WrapperData {
1150 original_release: schema.release(),
1151 original_private_data: schema.private_data(),
1152 });
1153 unsafe { schema.set_release(Some(wrapping_release)) };
1154 unsafe { schema.set_private_data(Box::into_raw(data).cast::<c_void>()) };
1155
1156 drop(schema); assert!(WRAPPER_RAN.load(Ordering::SeqCst));
1158 }
1159}