@@ -7,6 +7,10 @@ use std::ffi::c_void;
77use std:: ptr;
88use std:: sync:: Arc ;
99
10+ use arrow_array:: array:: make_array;
11+ use arrow_array:: ffi:: FFI_ArrowArray ;
12+ use arrow_array:: ffi:: FFI_ArrowSchema ;
13+ use arrow_array:: ffi:: from_ffi;
1014use paste:: paste;
1115use vortex:: array:: ArrayRef ;
1216use vortex:: array:: IntoArray ;
@@ -16,6 +20,7 @@ use vortex::array::arrays::NullArray;
1620use vortex:: array:: arrays:: PrimitiveArray ;
1721use vortex:: array:: arrays:: StructArray ;
1822use vortex:: array:: arrays:: struct_:: StructArrayExt ;
23+ use vortex:: array:: arrow:: FromArrowArray ;
1924use vortex:: array:: validity:: Validity ;
2025use vortex:: buffer:: Buffer ;
2126use vortex:: dtype:: DType ;
@@ -326,6 +331,49 @@ pub extern "C-unwind" fn vx_array_new_primitive(
326331 }
327332}
328333
334+ /// Create a Vortex array by importing an Arrow array via the Arrow C Data Interface.
335+ ///
336+ /// `array` and `schema` together describe a single Arrow array (the standard Arrow C Data
337+ /// Interface pair, e.g. as produced by exporting a record batch). Both are *consumed*: their
338+ /// `release` callbacks are invoked by this function and the caller must not use or release them
339+ /// afterwards.
340+ ///
341+ /// `nullable` controls the top-level nullability of the resulting array's dtype. For an Arrow
342+ /// record batch (which has no top-level validity) pass `false`.
343+ ///
344+ /// The imported buffers are referenced zero-copy where possible; the returned array keeps the
345+ /// Arrow data alive until it is freed with [`vx_array_free`].
346+ ///
347+ /// On error, returns NULL and sets `error_out`.
348+ ///
349+ /// Example:
350+ ///
351+ /// // export an Arrow record batch into (array, schema), then:
352+ /// vx_error* error = NULL;
353+ /// const vx_array* vx = vx_array_from_arrow(&array, &schema, false, &error);
354+ /// // ... push it to a sink or write it ...
355+ /// vx_array_free(vx);
356+ ///
357+ #[ unsafe( no_mangle) ]
358+ pub unsafe extern "C-unwind" fn vx_array_from_arrow (
359+ array : * mut FFI_ArrowArray ,
360+ schema : * mut FFI_ArrowSchema ,
361+ nullable : bool ,
362+ error_out : * mut * mut vx_error ,
363+ ) -> * const vx_array {
364+ try_or_default ( error_out, || {
365+ vortex_ensure ! ( !array. is_null( ) , "null arrow array" ) ;
366+ vortex_ensure ! ( !schema. is_null( ) , "null arrow schema" ) ;
367+ let ffi_array = unsafe { ptr:: replace ( array, FFI_ArrowArray :: empty ( ) ) } ;
368+ let ffi_schema = unsafe { ptr:: replace ( schema, FFI_ArrowSchema :: empty ( ) ) } ;
369+ let array_data = unsafe { from_ffi ( ffi_array, & ffi_schema) } ?;
370+ drop ( ffi_schema) ;
371+ let arrow_array = make_array ( array_data) ;
372+ let vortex_array = ArrayRef :: from_arrow ( arrow_array. as_ref ( ) , nullable) ?;
373+ Ok ( vx_array:: new ( Arc :: new ( vortex_array) ) )
374+ } )
375+ }
376+
329377macro_rules! ffiarray_get_ptype {
330378 ( $ptype: ident) => {
331379 paste! {
@@ -809,4 +857,67 @@ mod tests {
809857 // Note: dtype_ptr is now invalid - this test documents the lifetime pattern
810858 // In real usage, don't access dtype_ptr after freeing the array
811859 }
860+
861+ #[ test]
862+ #[ cfg_attr( miri, ignore) ]
863+ fn test_from_arrow_roundtrip ( ) {
864+ use arrow_array:: Array as ArrowArrayTrait ;
865+ use arrow_array:: Int32Array ;
866+ use arrow_array:: RecordBatch ;
867+ use arrow_array:: StringArray ;
868+ use arrow_array:: ffi:: to_ffi;
869+ use arrow_schema:: DataType ;
870+ use arrow_schema:: Field ;
871+ use arrow_schema:: Schema as ArrowSchema ;
872+
873+ let schema = Arc :: new ( ArrowSchema :: new ( vec ! [
874+ Field :: new( "a" , DataType :: Int32 , false ) ,
875+ Field :: new( "b" , DataType :: Utf8 , true ) ,
876+ ] ) ) ;
877+ let batch = RecordBatch :: try_new (
878+ schema,
879+ vec ! [
880+ Arc :: new( Int32Array :: from( vec![ 1 , 2 , 3 ] ) ) ,
881+ Arc :: new( StringArray :: from( vec![ Some ( "x" ) , None , Some ( "z" ) ] ) ) ,
882+ ] ,
883+ )
884+ . unwrap ( ) ;
885+
886+ let data = ArrowArrayTrait :: into_data ( arrow_array:: StructArray :: from ( batch) ) ;
887+ let ( mut ffi_array, mut ffi_schema) = to_ffi ( & data) . unwrap ( ) ;
888+
889+ let mut error = ptr:: null_mut ( ) ;
890+ let vx = unsafe {
891+ vx_array_from_arrow (
892+ & raw mut ffi_array,
893+ & raw mut ffi_schema,
894+ false ,
895+ & raw mut error,
896+ )
897+ } ;
898+ assert_no_error ( error) ;
899+ assert ! ( !vx. is_null( ) ) ;
900+
901+ unsafe {
902+ assert ! ( vx_array_has_dtype( vx, vx_dtype_variant:: DTYPE_STRUCT ) ) ;
903+ assert_eq ! ( vx_array_len( vx) , 3 ) ;
904+ assert ! ( !vx_array_is_nullable( vx) ) ;
905+
906+ let a = vx_array_get_field ( vx, 0 , & raw mut error) ;
907+ assert_no_error ( error) ;
908+ assert ! ( vx_array_is_primitive( a, vx_ptype:: PTYPE_I32 ) ) ;
909+ assert_eq ! ( vx_array_get_i32( a, 0 ) , 1 ) ;
910+ assert_eq ! ( vx_array_get_i32( a, 2 ) , 3 ) ;
911+ vx_array_free ( a) ;
912+
913+ let b = vx_array_get_field ( vx, 1 , & raw mut error) ;
914+ assert_no_error ( error) ;
915+ assert ! ( vx_array_has_dtype( b, vx_dtype_variant:: DTYPE_UTF8 ) ) ;
916+ assert ! ( vx_array_element_is_invalid( b, 1 , & raw mut error) ) ;
917+ assert_no_error ( error) ;
918+ vx_array_free ( b) ;
919+
920+ vx_array_free ( vx) ;
921+ }
922+ }
812923}
0 commit comments