@@ -545,4 +545,220 @@ void testEmptyArray() {
545545 assertThat (array ).isNotNull ();
546546 assertThat (array .size ()).isEqualTo (0 );
547547 }
548+
549+ @ Test
550+ void testNestedRowType () {
551+ int tableBucket = 0 ;
552+
553+ // Build nested row types
554+ org .apache .paimon .types .RowType simpleNestedRowType =
555+ org .apache .paimon .types .RowType .builder ()
556+ .field ("nested_int" , new org .apache .paimon .types .IntType ())
557+ .field ("nested_string" , new org .apache .paimon .types .VarCharType ())
558+ .build ();
559+
560+ org .apache .paimon .types .RowType allTypesNestedRowType =
561+ org .apache .paimon .types .RowType .builder ()
562+ .field ("bool_field" , new org .apache .paimon .types .BooleanType ())
563+ .field ("byte_field" , new org .apache .paimon .types .TinyIntType ())
564+ .field ("short_field" , new org .apache .paimon .types .SmallIntType ())
565+ .field ("int_field" , new org .apache .paimon .types .IntType ())
566+ .field ("long_field" , new org .apache .paimon .types .BigIntType ())
567+ .field ("float_field" , new org .apache .paimon .types .FloatType ())
568+ .field ("double_field" , new org .apache .paimon .types .DoubleType ())
569+ .field ("string_field" , new org .apache .paimon .types .VarCharType ())
570+ .build ();
571+
572+ org .apache .paimon .types .RowType decimalTimestampRowType =
573+ org .apache .paimon .types .RowType .builder ()
574+ .field ("decimal_field" , new org .apache .paimon .types .DecimalType (10 , 2 ))
575+ .field ("timestamp_field" , new org .apache .paimon .types .TimestampType (3 ))
576+ .build ();
577+
578+ org .apache .paimon .types .RowType innerRowType =
579+ org .apache .paimon .types .RowType .builder ()
580+ .field ("inner_value" , new org .apache .paimon .types .IntType ())
581+ .build ();
582+
583+ org .apache .paimon .types .RowType middleRowType =
584+ org .apache .paimon .types .RowType .builder ()
585+ .field ("middle_int" , new org .apache .paimon .types .IntType ())
586+ .field ("inner_row" , innerRowType )
587+ .build ();
588+
589+ org .apache .paimon .types .RowType rowWithArrayType =
590+ org .apache .paimon .types .RowType .builder ()
591+ .field ("id" , new org .apache .paimon .types .IntType ())
592+ .field (
593+ "values" ,
594+ new org .apache .paimon .types .ArrayType (
595+ new org .apache .paimon .types .IntType ()))
596+ .build ();
597+
598+ org .apache .paimon .types .RowType arrayElementRowType =
599+ org .apache .paimon .types .RowType .builder ()
600+ .field ("id" , new org .apache .paimon .types .IntType ())
601+ .field ("name" , new org .apache .paimon .types .VarCharType ())
602+ .build ();
603+
604+ org .apache .paimon .types .RowType nullableFieldsRowType =
605+ org .apache .paimon .types .RowType .builder ()
606+ .field ("id" , new org .apache .paimon .types .IntType ())
607+ .field (
608+ "nullable_field" ,
609+ new org .apache .paimon .types .VarCharType ().nullable ())
610+ .build ();
611+
612+ RowType tableRowType =
613+ RowType .of (
614+ simpleNestedRowType ,
615+ allTypesNestedRowType ,
616+ decimalTimestampRowType ,
617+ middleRowType ,
618+ rowWithArrayType ,
619+ new org .apache .paimon .types .ArrayType (arrayElementRowType ),
620+ simpleNestedRowType .nullable (),
621+ nullableFieldsRowType ,
622+ // system columns
623+ new org .apache .paimon .types .IntType (),
624+ new org .apache .paimon .types .BigIntType (),
625+ new org .apache .paimon .types .LocalZonedTimestampType (3 ));
626+
627+ FlussRecordAsPaimonRow flussRecordAsPaimonRow =
628+ new FlussRecordAsPaimonRow (tableBucket , tableRowType );
629+ long logOffset = 0 ;
630+ long timeStamp = System .currentTimeMillis ();
631+ GenericRow genericRow = new GenericRow (8 );
632+
633+ // Simple nested row
634+ GenericRow simpleNestedRow = new GenericRow (2 );
635+ simpleNestedRow .setField (0 , 100 );
636+ simpleNestedRow .setField (1 , BinaryString .fromString ("nested_value" ));
637+ genericRow .setField (0 , simpleNestedRow );
638+
639+ // Nested row with all primitive types
640+ GenericRow allTypesNestedRow = new GenericRow (8 );
641+ allTypesNestedRow .setField (0 , true );
642+ allTypesNestedRow .setField (1 , (byte ) 127 );
643+ allTypesNestedRow .setField (2 , (short ) 32000 );
644+ allTypesNestedRow .setField (3 , 2147483647 );
645+ allTypesNestedRow .setField (4 , 9223372036854775807L );
646+ allTypesNestedRow .setField (5 , 3.14f );
647+ allTypesNestedRow .setField (6 , 2.718281828 );
648+ allTypesNestedRow .setField (7 , BinaryString .fromString ("test_string" ));
649+ genericRow .setField (1 , allTypesNestedRow );
650+
651+ // Nested row with decimal and timestamp
652+ GenericRow decimalTimestampRow = new GenericRow (2 );
653+ decimalTimestampRow .setField (0 , Decimal .fromBigDecimal (new BigDecimal ("123.45" ), 10 , 2 ));
654+ decimalTimestampRow .setField (1 , TimestampNtz .fromMillis (1698235273182L ));
655+ genericRow .setField (2 , decimalTimestampRow );
656+
657+ // Deeply nested row
658+ GenericRow innerRow = new GenericRow (1 );
659+ innerRow .setField (0 , 999 );
660+ GenericRow middleRow = new GenericRow (2 );
661+ middleRow .setField (0 , 500 );
662+ middleRow .setField (1 , innerRow );
663+ genericRow .setField (3 , middleRow );
664+
665+ // Nested row with array field
666+ GenericRow nestedRowWithArray = new GenericRow (2 );
667+ nestedRowWithArray .setField (0 , 123 );
668+ nestedRowWithArray .setField (1 , new GenericArray (new int [] {10 , 20 , 30 }));
669+ genericRow .setField (4 , nestedRowWithArray );
670+
671+ // Array of nested rows
672+ GenericRow arrayRow1 = new GenericRow (2 );
673+ arrayRow1 .setField (0 , 1 );
674+ arrayRow1 .setField (1 , BinaryString .fromString ("Alice" ));
675+ GenericRow arrayRow2 = new GenericRow (2 );
676+ arrayRow2 .setField (0 , 2 );
677+ arrayRow2 .setField (1 , BinaryString .fromString ("Bob" ));
678+ genericRow .setField (5 , new GenericArray (new Object [] {arrayRow1 , arrayRow2 }));
679+
680+ // Null nested row
681+ genericRow .setField (6 , null );
682+
683+ // Nested row with nullable fields
684+ GenericRow nullableFieldsRow = new GenericRow (2 );
685+ nullableFieldsRow .setField (0 , 42 );
686+ nullableFieldsRow .setField (1 , null );
687+ genericRow .setField (7 , nullableFieldsRow );
688+
689+ LogRecord logRecord = new GenericRecord (logOffset , timeStamp , APPEND_ONLY , genericRow );
690+ flussRecordAsPaimonRow .setFlussRecord (logRecord );
691+
692+ // Test simple nested row
693+ org .apache .paimon .data .InternalRow paimonSimpleRow = flussRecordAsPaimonRow .getRow (0 , 2 );
694+ assertThat (paimonSimpleRow ).isNotNull ();
695+ assertThat (paimonSimpleRow .getInt (0 )).isEqualTo (100 );
696+ assertThat (paimonSimpleRow .getString (1 ).toString ()).isEqualTo ("nested_value" );
697+
698+ // Test nested row with all primitive types
699+ org .apache .paimon .data .InternalRow paimonAllTypesRow = flussRecordAsPaimonRow .getRow (1 , 8 );
700+ assertThat (paimonAllTypesRow ).isNotNull ();
701+ assertThat (paimonAllTypesRow .getBoolean (0 )).isTrue ();
702+ assertThat (paimonAllTypesRow .getByte (1 )).isEqualTo ((byte ) 127 );
703+ assertThat (paimonAllTypesRow .getShort (2 )).isEqualTo ((short ) 32000 );
704+ assertThat (paimonAllTypesRow .getInt (3 )).isEqualTo (2147483647 );
705+ assertThat (paimonAllTypesRow .getLong (4 )).isEqualTo (9223372036854775807L );
706+ assertThat (paimonAllTypesRow .getFloat (5 )).isEqualTo (3.14f );
707+ assertThat (paimonAllTypesRow .getDouble (6 )).isEqualTo (2.718281828 );
708+ assertThat (paimonAllTypesRow .getString (7 ).toString ()).isEqualTo ("test_string" );
709+
710+ // Test nested row with decimal and timestamp
711+ org .apache .paimon .data .InternalRow paimonDecimalTimestampRow =
712+ flussRecordAsPaimonRow .getRow (2 , 2 );
713+ assertThat (paimonDecimalTimestampRow ).isNotNull ();
714+ assertThat (paimonDecimalTimestampRow .getDecimal (0 , 10 , 2 ).toBigDecimal ())
715+ .isEqualTo (new BigDecimal ("123.45" ));
716+ assertThat (paimonDecimalTimestampRow .getTimestamp (1 , 3 ).getMillisecond ())
717+ .isEqualTo (1698235273182L );
718+
719+ // Test deeply nested row
720+ org .apache .paimon .data .InternalRow paimonMiddleRow = flussRecordAsPaimonRow .getRow (3 , 2 );
721+ assertThat (paimonMiddleRow ).isNotNull ();
722+ assertThat (paimonMiddleRow .getInt (0 )).isEqualTo (500 );
723+ org .apache .paimon .data .InternalRow paimonInnerRow = paimonMiddleRow .getRow (1 , 1 );
724+ assertThat (paimonInnerRow ).isNotNull ();
725+ assertThat (paimonInnerRow .getInt (0 )).isEqualTo (999 );
726+
727+ // Test nested row with array field
728+ org .apache .paimon .data .InternalRow paimonRowWithArray = flussRecordAsPaimonRow .getRow (4 , 2 );
729+ assertThat (paimonRowWithArray ).isNotNull ();
730+ assertThat (paimonRowWithArray .getInt (0 )).isEqualTo (123 );
731+ org .apache .paimon .data .InternalArray arrayInRow = paimonRowWithArray .getArray (1 );
732+ assertThat (arrayInRow ).isNotNull ();
733+ assertThat (arrayInRow .size ()).isEqualTo (3 );
734+ assertThat (arrayInRow .getInt (0 )).isEqualTo (10 );
735+ assertThat (arrayInRow .getInt (1 )).isEqualTo (20 );
736+ assertThat (arrayInRow .getInt (2 )).isEqualTo (30 );
737+
738+ // Test array of nested rows
739+ org .apache .paimon .data .InternalArray arrayOfRows = flussRecordAsPaimonRow .getArray (5 );
740+ assertThat (arrayOfRows ).isNotNull ();
741+ assertThat (arrayOfRows .size ()).isEqualTo (2 );
742+ org .apache .paimon .data .InternalRow paimonRow1 = arrayOfRows .getRow (0 , 2 );
743+ assertThat (paimonRow1 .getInt (0 )).isEqualTo (1 );
744+ assertThat (paimonRow1 .getString (1 ).toString ()).isEqualTo ("Alice" );
745+ org .apache .paimon .data .InternalRow paimonRow2 = arrayOfRows .getRow (1 , 2 );
746+ assertThat (paimonRow2 .getInt (0 )).isEqualTo (2 );
747+ assertThat (paimonRow2 .getString (1 ).toString ()).isEqualTo ("Bob" );
748+
749+ // Test null nested row
750+ assertThat (flussRecordAsPaimonRow .isNullAt (6 )).isTrue ();
751+
752+ // Test nested row with nullable fields
753+ org .apache .paimon .data .InternalRow paimonNullableFieldsRow =
754+ flussRecordAsPaimonRow .getRow (7 , 2 );
755+ assertThat (paimonNullableFieldsRow ).isNotNull ();
756+ assertThat (paimonNullableFieldsRow .getInt (0 )).isEqualTo (42 );
757+ assertThat (paimonNullableFieldsRow .isNullAt (1 )).isTrue ();
758+
759+ // Verify system columns
760+ assertThat (flussRecordAsPaimonRow .getInt (8 )).isEqualTo (tableBucket );
761+ assertThat (flussRecordAsPaimonRow .getLong (9 )).isEqualTo (logOffset );
762+ assertThat (flussRecordAsPaimonRow .getLong (10 )).isEqualTo (timeStamp );
763+ }
548764}
0 commit comments