This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 923e694d23 [common] Make BinaryRow.anyNull respect the row offset
(#9525)
923e694d23 is described below
commit 923e694d23937f46b3edc114f030d3153b4d2050
Author: YangJie <[email protected]>
AuthorDate: Wed Sep 2 02:40:14 2026 -0400
[common] Make BinaryRow.anyNull respect the row offset (#9525)
---
.../java/org/apache/paimon/data/BinaryRow.java | 4 +-
.../java/org/apache/paimon/data/BinaryRowTest.java | 80 ++++++++++++++++++++++
2 files changed, 82 insertions(+), 2 deletions(-)
diff --git a/paimon-common/src/main/java/org/apache/paimon/data/BinaryRow.java
b/paimon-common/src/main/java/org/apache/paimon/data/BinaryRow.java
index e7c522c8de..521fe50ed8 100644
--- a/paimon-common/src/main/java/org/apache/paimon/data/BinaryRow.java
+++ b/paimon-common/src/main/java/org/apache/paimon/data/BinaryRow.java
@@ -376,11 +376,11 @@ public final class BinaryRow extends BinarySection
implements InternalRow, DataS
/** The bit is 1 when the field is null. Default is 0. */
public boolean anyNull() {
// Skip the header.
- if ((segments[0].getLong(0) & FIRST_BYTE_ZERO) != 0) {
+ if ((segments[0].getLong(offset) & FIRST_BYTE_ZERO) != 0) {
return true;
}
for (int i = 8; i < nullBitsSizeInBytes; i += 8) {
- if (segments[0].getLong(i) != 0) {
+ if (segments[0].getLong(offset + i) != 0) {
return true;
}
}
diff --git
a/paimon-common/src/test/java/org/apache/paimon/data/BinaryRowTest.java
b/paimon-common/src/test/java/org/apache/paimon/data/BinaryRowTest.java
index edf562bc72..0d165accdd 100644
--- a/paimon-common/src/test/java/org/apache/paimon/data/BinaryRowTest.java
+++ b/paimon-common/src/test/java/org/apache/paimon/data/BinaryRowTest.java
@@ -324,6 +324,86 @@ public class BinaryRowTest {
}
}
+ @Test
+ public void testAnyNullWithNonZeroOffset() {
+ BinaryRow rowWithNull = new BinaryRow(1);
+ BinaryRowWriter writer = new BinaryRowWriter(rowWithNull);
+ writer.setNullAt(0);
+ writer.complete();
+
+ BinaryRow rowWithoutNull = new BinaryRow(1);
+ writer = new BinaryRowWriter(rowWithoutNull);
+ writer.writeInt(0, 42);
+ writer.complete();
+
+ // A four-byte pad in front, as BinaryRowSerializer leaves when it
points a row at the
+ // bytes following a length prefix, so the row offset is not a
multiple of eight either.
+ // The leading row is an INSERT row with no nulls, so a read that
starts at zero finds
+ // only zero bytes and reports no null.
+ int pad = 4;
+ MemorySegment segment = concat(pad, rowWithoutNull, rowWithNull);
+ int notNullLength = rowWithoutNull.getSizeInBytes();
+
+ BinaryRow atOffset = new BinaryRow(1);
+ atOffset.pointTo(segment, pad + notNullLength,
rowWithNull.getSizeInBytes());
+ assertThat(atOffset.isNullAt(0)).isTrue();
+ assertThat(atOffset.anyNull()).isTrue();
+
+ BinaryRow leading = new BinaryRow(1);
+ leading.pointTo(segment, pad, notNullLength);
+ assertThat(leading.anyNull()).isFalse();
+ }
+
+ @Test
+ public void testAnyNullHighFieldWithNonZeroOffset() {
+ // 60 fields push the null-bit set past the first 8-byte word, so the
loop in anyNull()
+ // has to honor the offset as well as the header read above it does.
+ int arity = 60;
+ int nullField = 59;
+ BinaryRow rowWithNull = new BinaryRow(arity);
+ BinaryRowWriter writer = new BinaryRowWriter(rowWithNull);
+ writer.setNullAt(nullField);
+ writer.complete();
+
+ BinaryRow rowWithoutNull = new BinaryRow(arity);
+ writer = new BinaryRowWriter(rowWithoutNull);
+ for (int i = 0; i < arity; i++) {
+ writer.writeInt(i, i);
+ }
+ writer.complete();
+
+ MemorySegment segment = concat(0, rowWithoutNull, rowWithNull);
+ int notNullLength = rowWithoutNull.getSizeInBytes();
+
+ BinaryRow atOffset = new BinaryRow(arity);
+ atOffset.pointTo(segment, notNullLength, rowWithNull.getSizeInBytes());
+ assertThat(atOffset.isNullAt(nullField)).isTrue();
+ assertThat(atOffset.anyNull()).isTrue();
+
+ BinaryRow leading = new BinaryRow(arity);
+ leading.pointTo(segment, 0, notNullLength);
+ assertThat(leading.anyNull()).isFalse();
+ }
+
+ /**
+ * Lays the rows out back to back in one segment behind {@code pad} bytes.
The row without nulls
+ * goes first, so a read that ignores the row offset lands on it and
reports no null.
+ */
+ private static MemorySegment concat(int pad, BinaryRow... rows) {
+ int size = pad;
+ for (BinaryRow row : rows) {
+ size += row.getSizeInBytes();
+ }
+ byte[] bytes = new byte[size];
+ int position = pad;
+ for (BinaryRow row : rows) {
+ byte[] rowBytes = row.toBytes();
+ System.arraycopy(rowBytes, 0, bytes, position, rowBytes.length);
+ position += rowBytes.length;
+ }
+ return MemorySegment.wrap(bytes);
+ }
+
@Test
public void testSingleSegmentBinaryRowHashCode() {
final Random rnd = new Random(System.currentTimeMillis());