@@ -2589,4 +2589,116 @@ record = datumReader.read(record, decoder);
25892589 }
25902590 }
25912591 }
2592+
2593+ @ Test
2594+ public void testWriteNullableStructs () throws Exception {
2595+
2596+ // Field definitions
2597+ FieldType structFieldType = new FieldType (false , new ArrowType .Struct (), null );
2598+ FieldType nullableStructFieldType = new FieldType (true , new ArrowType .Struct (), null );
2599+ Field intField = new Field ("intField" , FieldType .notNullable (new ArrowType .Int (32 , true )), null );
2600+ Field nullableIntField = new Field ("nullableIntField" , FieldType .nullable (new ArrowType .Int (32 , true )), null );
2601+ Field structField = new Field ("struct" , structFieldType , Arrays .asList (intField , nullableIntField ));
2602+ Field nullableStructField = new Field ("nullableStruct" , nullableStructFieldType , Arrays .asList (intField , nullableIntField ));
2603+
2604+ // Create empty vectors
2605+ BufferAllocator allocator = new RootAllocator ();
2606+ StructVector structVector = new StructVector ("struct" , allocator , structFieldType , null );
2607+ StructVector nullableStructVector = new StructVector ("nullableStruct" , allocator , nullableStructFieldType , null );
2608+ structVector .initializeChildrenFromFields (Arrays .asList (intField , nullableIntField ));
2609+ nullableStructVector .initializeChildrenFromFields (Arrays .asList (intField , nullableIntField ));
2610+ structVector .allocateNew ();
2611+ nullableStructVector .allocateNew ();
2612+
2613+ // Set up VSR
2614+ List <FieldVector > vectors = Arrays .asList (structVector , nullableStructVector );
2615+ int rowCount = 4 ;
2616+
2617+ try (VectorSchemaRoot root = new VectorSchemaRoot (vectors )) {
2618+
2619+ root .setRowCount (rowCount );
2620+ root .allocateNew ();
2621+
2622+ // Set test data for structVector
2623+ IntVector intVector = (IntVector ) structVector .getChild ("intField" );
2624+ IntVector nullableIntVector = (IntVector ) structVector .getChild ("nullableIntField" );
2625+ for (int i = 0 ; i < rowCount ; i ++) {
2626+ structVector .setIndexDefined (i );
2627+ intVector .setSafe (i , i );
2628+ if (i % 2 == 0 ) {
2629+ nullableIntVector .setSafe (i , i * 10 );
2630+ } else {
2631+ nullableIntVector .setNull (i );
2632+ }
2633+ }
2634+
2635+ // Set test data for nullableStructVector
2636+ IntVector nullableStructIntVector = (IntVector ) nullableStructVector .getChild ("intField" );
2637+ IntVector nullableStructNullableIntVector = (IntVector ) nullableStructVector .getChild ("nullableIntField" );
2638+ for (int i = 0 ; i < rowCount ; i ++) {
2639+ if (i >= 2 ) {
2640+ nullableStructVector .setIndexDefined (i );
2641+ nullableStructIntVector .setSafe (i , i );
2642+ if (i % 2 == 0 ) {
2643+ nullableStructNullableIntVector .setSafe (i , i * 10 );
2644+ } else {
2645+ nullableStructNullableIntVector .setNull (i );
2646+ }
2647+ }else {
2648+ nullableStructVector .setNull (i );
2649+ }
2650+ }
2651+
2652+ File dataFile = new File (TMP , "testWriteNullableStructs.avro" );
2653+
2654+ // Write an AVRO block using the producer classes
2655+ try (FileOutputStream fos = new FileOutputStream (dataFile )) {
2656+ BinaryEncoder encoder = new EncoderFactory ().directBinaryEncoder (fos , null );
2657+ CompositeAvroProducer producer = ArrowToAvroUtils .createCompositeProducer (vectors );
2658+ for (int row = 0 ; row < rowCount ; row ++) {
2659+ producer .produce (encoder );
2660+ }
2661+ encoder .flush ();
2662+ }
2663+
2664+ // Set up reading the AVRO block as a GenericRecord
2665+ Schema schema = ArrowToAvroUtils .createAvroSchema (root .getSchema ().getFields ());
2666+ GenericDatumReader <GenericRecord > datumReader = new GenericDatumReader <>(schema );
2667+
2668+ try (InputStream inputStream = new FileInputStream (dataFile )) {
2669+
2670+ BinaryDecoder decoder = DecoderFactory .get ().binaryDecoder (inputStream , null );
2671+ GenericRecord record = null ;
2672+
2673+ // Read and check values
2674+ for (int row = 0 ; row < rowCount ; row ++) {
2675+ record = datumReader .read (record , decoder );
2676+ if (row % 2 == 0 ) {
2677+ assertNotNull (record .get ("struct" ));
2678+ GenericRecord structRecord = (GenericRecord ) record .get ("struct" );
2679+ assertEquals (row , structRecord .get ("intField" ));
2680+ assertEquals (row * 10 , structRecord .get ("nullableIntField" ));
2681+ } else {
2682+ assertNotNull (record .get ("struct" ));
2683+ GenericRecord structRecord = (GenericRecord ) record .get ("struct" );
2684+ assertEquals (row , structRecord .get ("intField" ));
2685+ assertNull (structRecord .get ("nullableIntField" ));
2686+
2687+ }
2688+ if (row >= 2 ) {
2689+ assertNotNull (record .get ("nullableStruct" ));
2690+ GenericRecord nullableStructRecord = (GenericRecord ) record .get ("nullableStruct" );
2691+ assertEquals (row , nullableStructRecord .get ("intField" ));
2692+ if (row % 2 == 0 ) {
2693+ assertEquals (row * 10 , nullableStructRecord .get ("nullableIntField" ));
2694+ } else {
2695+ assertNull (nullableStructRecord .get ("nullableIntField" ));
2696+ }
2697+ } else {
2698+ assertNull (record .get ("nullableStruct" ));
2699+ }
2700+ }
2701+ }
2702+ }
2703+ }
25922704}
0 commit comments