2424import org .apache .paimon .data .BinaryRowWriter ;
2525import org .apache .paimon .data .BinaryWriter ;
2626import org .apache .paimon .data .BinaryWriter .ValueSetter ;
27+ import org .apache .paimon .data .Decimal ;
2728import org .apache .paimon .data .GenericRow ;
2829import org .apache .paimon .data .InternalRow ;
2930import org .apache .paimon .data .InternalRow .FieldGetter ;
3031import org .apache .paimon .data .NestedRow ;
32+ import org .apache .paimon .data .Timestamp ;
3133import org .apache .paimon .io .DataInputView ;
3234import org .apache .paimon .io .DataOutputView ;
3335import org .apache .paimon .types .DataType ;
36+ import org .apache .paimon .types .DataTypeChecks ;
37+ import org .apache .paimon .types .DecimalType ;
38+ import org .apache .paimon .types .LocalZonedTimestampType ;
3439import org .apache .paimon .types .RowType ;
40+ import org .apache .paimon .types .TimestampType ;
3541
3642import java .io .IOException ;
3743import java .util .Arrays ;
@@ -46,10 +52,20 @@ public class InternalRowSerializer extends AbstractRowDataSerializer<InternalRow
4652 private final Serializer [] fieldSerializers ;
4753 private final FieldGetter [] fieldGetters ;
4854 private final ValueSetter [] valueSetters ;
55+ private final boolean [] writeNulls ;
4956
5057 private transient BinaryRow reuseRow ;
5158 private transient BinaryRowWriter reuseWriter ;
5259
60+ public InternalRowSerializer (RowType rowType , boolean isBucketKeySerializer ) {
61+ this (
62+ rowType .getFieldTypes ().toArray (new DataType [0 ]),
63+ rowType .getFieldTypes ().stream ()
64+ .map (InternalSerializers ::create )
65+ .toArray (Serializer []::new ),
66+ isBucketKeySerializer );
67+ }
68+
5369 public InternalRowSerializer (RowType rowType ) {
5470 this (
5571 rowType .getFieldTypes ().toArray (new DataType [0 ]),
@@ -65,16 +81,34 @@ public InternalRowSerializer(DataType... types) {
6581 }
6682
6783 public InternalRowSerializer (DataType [] types , Serializer <?>[] fieldSerializers ) {
84+ this (types , fieldSerializers , false );
85+ }
86+
87+ private InternalRowSerializer (
88+ DataType [] types , Serializer <?>[] fieldSerializers , boolean isBucketKeySerializer ) {
6889 this .types = types ;
6990 this .fieldSerializers = fieldSerializers ;
7091 this .binarySerializer = new BinaryRowSerializer (types .length );
7192 this .fieldGetters = new FieldGetter [types .length ];
7293 this .valueSetters = new ValueSetter [types .length ];
94+ this .writeNulls = new boolean [types .length ];
95+ Arrays .fill (writeNulls , true );
7396 for (int i = 0 ; i < types .length ; i ++) {
7497 DataType type = types [i ];
7598 fieldGetters [i ] = InternalRow .createFieldGetter (type , i );
7699 // pass serializer to avoid infinite loop
77100 valueSetters [i ] = BinaryWriter .createValueSetter (type , fieldSerializers [i ]);
101+
102+ // BucketKey projection use different write logic for non-compact type nulls,
103+ // reference: org.apache.paimon.codegen.GenerateUtils.binaryWriterWriteNull
104+ if (isBucketKeySerializer ) {
105+ if (type instanceof DecimalType ) {
106+ writeNulls [i ] = Decimal .isCompact (DataTypeChecks .getPrecision (type ));
107+ } else if (type instanceof TimestampType
108+ || type instanceof LocalZonedTimestampType ) {
109+ writeNulls [i ] = Timestamp .isCompact (DataTypeChecks .getPrecision (type ));
110+ }
111+ }
78112 }
79113 }
80114
@@ -157,7 +191,7 @@ public BinaryRow toBinaryRow(InternalRow row) {
157191 reuseWriter .writeRowKind (row .getRowKind ());
158192 for (int i = 0 ; i < types .length ; i ++) {
159193 Object field = fieldGetters [i ].getFieldOrNull (row );
160- if (field == null ) {
194+ if (field == null && writeNulls [ i ] ) {
161195 reuseWriter .setNullAt (i );
162196 } else {
163197 valueSetters [i ].setValue (reuseWriter , i , field );
0 commit comments