@@ -268,13 +268,7 @@ pub fn record_batch_to_duckdb_data_chunk(
268
268
fn primitive_array_to_flat_vector < T : ArrowPrimitiveType > ( array : & PrimitiveArray < T > , out_vector : & mut FlatVector ) {
269
269
// assert!(array.len() <= out_vector.capacity());
270
270
out_vector. copy :: < T :: Native > ( array. values ( ) ) ;
271
- if let Some ( nulls) = array. nulls ( ) {
272
- for ( i, null) in nulls. into_iter ( ) . enumerate ( ) {
273
- if !null {
274
- out_vector. set_null ( i) ;
275
- }
276
- }
277
- }
271
+ set_nulls_in_flat_vector ( array, out_vector) ;
278
272
}
279
273
280
274
fn primitive_array_to_flat_vector_cast < T : ArrowPrimitiveType > (
@@ -285,13 +279,7 @@ fn primitive_array_to_flat_vector_cast<T: ArrowPrimitiveType>(
285
279
let array = arrow:: compute:: kernels:: cast:: cast ( array, & data_type) . unwrap ( ) ;
286
280
let out_vector: & mut FlatVector = out_vector. as_mut_any ( ) . downcast_mut ( ) . unwrap ( ) ;
287
281
out_vector. copy :: < T :: Native > ( array. as_primitive :: < T > ( ) . values ( ) ) ;
288
- if let Some ( nulls) = array. nulls ( ) {
289
- for ( i, null) in nulls. iter ( ) . enumerate ( ) {
290
- if !null {
291
- out_vector. set_null ( i) ;
292
- }
293
- }
294
- }
282
+ set_nulls_in_flat_vector ( & array, out_vector) ;
295
283
}
296
284
297
285
fn primitive_array_to_vector ( array : & dyn Array , out : & mut dyn Vector ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
@@ -441,13 +429,7 @@ fn decimal_array_to_vector(array: &Decimal128Array, out: &mut FlatVector, width:
441
429
}
442
430
443
431
// Set nulls
444
- if let Some ( nulls) = array. nulls ( ) {
445
- for ( i, null) in nulls. into_iter ( ) . enumerate ( ) {
446
- if !null {
447
- out. set_null ( i) ;
448
- }
449
- }
450
- }
432
+ set_nulls_in_flat_vector ( array, out) ;
451
433
}
452
434
453
435
/// Convert Arrow [BooleanArray] to a duckdb vector.
@@ -457,6 +439,7 @@ fn boolean_array_to_vector(array: &BooleanArray, out: &mut FlatVector) {
457
439
for i in 0 ..array. len ( ) {
458
440
out. as_mut_slice ( ) [ i] = array. value ( i) ;
459
441
}
442
+ set_nulls_in_flat_vector ( array, out) ;
460
443
}
461
444
462
445
fn string_array_to_vector < O : OffsetSizeTrait > ( array : & GenericStringArray < O > , out : & mut FlatVector ) {
@@ -467,6 +450,7 @@ fn string_array_to_vector<O: OffsetSizeTrait>(array: &GenericStringArray<O>, out
467
450
let s = array. value ( i) ;
468
451
out. insert ( i, s) ;
469
452
}
453
+ set_nulls_in_flat_vector ( array, out) ;
470
454
}
471
455
472
456
fn binary_array_to_vector ( array : & BinaryArray , out : & mut FlatVector ) {
@@ -476,6 +460,7 @@ fn binary_array_to_vector(array: &BinaryArray, out: &mut FlatVector) {
476
460
let s = array. value ( i) ;
477
461
out. insert ( i, s) ;
478
462
}
463
+ set_nulls_in_flat_vector ( array, out) ;
479
464
}
480
465
481
466
fn list_array_to_vector < O : OffsetSizeTrait + AsPrimitive < usize > > (
@@ -504,6 +489,8 @@ fn list_array_to_vector<O: OffsetSizeTrait + AsPrimitive<usize>>(
504
489
let length = array. value_length ( i) ;
505
490
out. set_entry ( i, offset. as_ ( ) , length. as_ ( ) ) ;
506
491
}
492
+ set_nulls_in_list_vector ( array, out) ;
493
+
507
494
Ok ( ( ) )
508
495
}
509
496
@@ -528,6 +515,8 @@ fn fixed_size_list_array_to_vector(
528
515
}
529
516
}
530
517
518
+ set_nulls_in_array_vector ( array, out) ;
519
+
531
520
Ok ( ( ) )
532
521
}
533
522
@@ -575,6 +564,7 @@ fn struct_array_to_vector(array: &StructArray, out: &mut StructVector) -> Result
575
564
}
576
565
}
577
566
}
567
+ set_nulls_in_struct_vector ( array, out) ;
578
568
Ok ( ( ) )
579
569
}
580
570
@@ -611,6 +601,46 @@ pub fn arrow_ffi_to_query_params(array: FFI_ArrowArray, schema: FFI_ArrowSchema)
611
601
[ arr as * mut _ as usize , sch as * mut _ as usize ]
612
602
}
613
603
604
+ fn set_nulls_in_flat_vector ( array : & dyn Array , out_vector : & mut FlatVector ) {
605
+ if let Some ( nulls) = array. nulls ( ) {
606
+ for ( i, null) in nulls. into_iter ( ) . enumerate ( ) {
607
+ if !null {
608
+ out_vector. set_null ( i) ;
609
+ }
610
+ }
611
+ }
612
+ }
613
+
614
+ fn set_nulls_in_struct_vector ( array : & dyn Array , out_vector : & mut StructVector ) {
615
+ if let Some ( nulls) = array. nulls ( ) {
616
+ for ( i, null) in nulls. into_iter ( ) . enumerate ( ) {
617
+ if !null {
618
+ out_vector. set_null ( i) ;
619
+ }
620
+ }
621
+ }
622
+ }
623
+
624
+ fn set_nulls_in_array_vector ( array : & dyn Array , out_vector : & mut ArrayVector ) {
625
+ if let Some ( nulls) = array. nulls ( ) {
626
+ for ( i, null) in nulls. into_iter ( ) . enumerate ( ) {
627
+ if !null {
628
+ out_vector. set_null ( i) ;
629
+ }
630
+ }
631
+ }
632
+ }
633
+
634
+ fn set_nulls_in_list_vector ( array : & dyn Array , out_vector : & mut ListVector ) {
635
+ if let Some ( nulls) = array. nulls ( ) {
636
+ for ( i, null) in nulls. into_iter ( ) . enumerate ( ) {
637
+ if !null {
638
+ out_vector. set_null ( i) ;
639
+ }
640
+ }
641
+ }
642
+ }
643
+
614
644
#[ cfg( test) ]
615
645
mod test {
616
646
use super :: { arrow_recordbatch_to_query_params, ArrowVTab } ;
@@ -705,6 +735,44 @@ mod test {
705
735
Ok ( ( ) )
706
736
}
707
737
738
+ #[ test]
739
+ fn test_append_struct_contains_null ( ) -> Result < ( ) , Box < dyn Error > > {
740
+ let db = Connection :: open_in_memory ( ) ?;
741
+ db. execute_batch ( "CREATE TABLE t1 (s STRUCT(v VARCHAR, i INTEGER))" ) ?;
742
+ {
743
+ let struct_array = StructArray :: try_new (
744
+ vec ! [
745
+ Arc :: new( Field :: new( "v" , DataType :: Utf8 , true ) ) ,
746
+ Arc :: new( Field :: new( "i" , DataType :: Int32 , true ) ) ,
747
+ ]
748
+ . into ( ) ,
749
+ vec ! [
750
+ Arc :: new( StringArray :: from( vec![ Some ( "foo" ) , Some ( "bar" ) ] ) ) as ArrayRef ,
751
+ Arc :: new( Int32Array :: from( vec![ Some ( 1 ) , Some ( 2 ) ] ) ) as ArrayRef ,
752
+ ] ,
753
+ Some ( vec ! [ true , false ] . into ( ) ) ,
754
+ ) ?;
755
+
756
+ let schema = Schema :: new ( vec ! [ Field :: new(
757
+ "s" ,
758
+ DataType :: Struct ( Fields :: from( vec![
759
+ Field :: new( "v" , DataType :: Utf8 , true ) ,
760
+ Field :: new( "i" , DataType :: Int32 , true ) ,
761
+ ] ) ) ,
762
+ true ,
763
+ ) ] ) ;
764
+
765
+ let record_batch = RecordBatch :: try_new ( Arc :: new ( schema) , vec ! [ Arc :: new( struct_array) ] ) ?;
766
+ let mut app = db. appender ( "t1" ) ?;
767
+ app. append_record_batch ( record_batch) ?;
768
+ }
769
+ let mut stmt = db. prepare ( "SELECT s FROM t1 where s IS NOT NULL" ) ?;
770
+ let rbs: Vec < RecordBatch > = stmt. query_arrow ( [ ] ) ?. collect ( ) ;
771
+ assert_eq ! ( rbs. iter( ) . map( |op| op. num_rows( ) ) . sum:: <usize >( ) , 1 ) ;
772
+
773
+ Ok ( ( ) )
774
+ }
775
+
708
776
fn check_rust_primitive_array_roundtrip < T1 , T2 > (
709
777
input_array : PrimitiveArray < T1 > ,
710
778
expected_array : PrimitiveArray < T2 > ,
@@ -762,7 +830,7 @@ mod test {
762
830
db. register_table_function :: < ArrowVTab > ( "arrow" ) ?;
763
831
764
832
// Roundtrip a record batch from Rust to DuckDB and back to Rust
765
- let schema = Schema :: new ( vec ! [ Field :: new( "a" , arry. data_type( ) . clone( ) , false ) ] ) ;
833
+ let schema = Schema :: new ( vec ! [ Field :: new( "a" , arry. data_type( ) . clone( ) , true ) ] ) ;
766
834
767
835
let rb = RecordBatch :: try_new ( Arc :: new ( schema) , vec ! [ Arc :: new( arry. clone( ) ) ] ) ?;
768
836
let param = arrow_recordbatch_to_query_params ( rb) ;
@@ -910,6 +978,24 @@ mod test {
910
978
Ok ( ( ) )
911
979
}
912
980
981
+ #[ test]
982
+ fn test_check_generic_array_roundtrip_contains_null ( ) -> Result < ( ) , Box < dyn Error > > {
983
+ check_generic_array_roundtrip ( ListArray :: new (
984
+ Arc :: new ( Field :: new ( "item" , DataType :: Utf8 , true ) ) ,
985
+ OffsetBuffer :: new ( ScalarBuffer :: from ( vec ! [ 0 , 2 , 4 , 5 ] ) ) ,
986
+ Arc :: new ( StringArray :: from ( vec ! [
987
+ Some ( "foo" ) ,
988
+ Some ( "baz" ) ,
989
+ Some ( "bar" ) ,
990
+ Some ( "foo" ) ,
991
+ Some ( "baz" ) ,
992
+ ] ) ) ,
993
+ Some ( vec ! [ true , false , true ] . into ( ) ) ,
994
+ ) ) ?;
995
+
996
+ Ok ( ( ) )
997
+ }
998
+
913
999
#[ test]
914
1000
fn test_utf8_roundtrip ( ) -> Result < ( ) , Box < dyn Error > > {
915
1001
check_generic_byte_roundtrip (
0 commit comments