From 447f2b5cdda77bd50e9c6b239a5b4aa922dd21c5 Mon Sep 17 00:00:00 2001 From: Mariamawit Berta Date: Fri, 17 Jul 2026 15:36:26 -0700 Subject: [PATCH 1/3] GH-559: Add reproduction test for FixedSizeBinary ComplexCopier bug --- .../complex/impl/TestComplexCopier.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java index b2a8cf9ba4..9bff2a534f 100644 --- a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java +++ b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java @@ -21,6 +21,8 @@ import java.math.BigDecimal; import java.util.UUID; + +import org.apache.arrow.memory.ArrowBuf; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; import org.apache.arrow.vector.DecimalVector; @@ -36,6 +38,7 @@ import org.apache.arrow.vector.complex.writer.FieldWriter; import org.apache.arrow.vector.extension.UuidType; import org.apache.arrow.vector.holders.DecimalHolder; +import org.apache.arrow.vector.holders.FixedSizeBinaryHolder; import org.apache.arrow.vector.types.Types; import org.apache.arrow.vector.types.pojo.ArrowType; import org.apache.arrow.vector.types.pojo.FieldType; @@ -207,6 +210,53 @@ public void testCopyListVector() { } } + @Test + public void testCopyListOfFixedSizeBinary() { + final int byteWidth = 4; + try (ListVector from = ListVector.empty("v", allocator); + ListVector to = ListVector.empty("v", allocator); + ArrowBuf buf = allocator.buffer(byteWidth)) { + + from.addOrGetVector(FieldType.nullable(new ArrowType.FixedSizeBinary(byteWidth))); + + UnionListWriter listWriter = from.getWriter(); + listWriter.allocate(); + + FixedSizeBinaryHolder holder = new FixedSizeBinaryHolder(); + holder.byteWidth = byteWidth; + holder.buffer = buf; + + for (int i = 0; i < COUNT; i++) { + listWriter.setPosition(i); + listWriter.startList(); + + buf.setBytes(0, new byte[] {1, 2, 3, 4}); + listWriter.fixedSizeBinary().write(holder); + + buf.setBytes(0, new byte[] {5, 6, 7, 8}); + listWriter.fixedSizeBinary().write(holder); + + listWriter.endList(); + } + from.setValueCount(COUNT); + + // copy values — this currently throws UnsupportedOperationException: FIXEDSIZEBINARY + FieldReader in = from.getReader(); + FieldWriter out = to.getWriter(); + UnsupportedOperationException e = + assertThrows( + UnsupportedOperationException.class, + () -> { + for (int i = 0; i < COUNT; i++) { + in.setPosition(i); + out.setPosition(i); + ComplexCopier.copy(in, out); + } + }); + assertTrue(e.getMessage().contains("FIXEDSIZEBINARY")); + } + } + @Test public void testCopyListVectorToANonEmptyList() { try (ListVector from = ListVector.empty("v", allocator); From 4720c1beb6741948cea52325420241f1b361b062 Mon Sep 17 00:00:00 2001 From: Mariamawit Berta Date: Tue, 28 Jul 2026 14:02:29 -0700 Subject: [PATCH 2/3] GH-559: [Java] Add FixedSizeBinary support to ComplexCopier --- .../main/codegen/templates/ComplexCopier.java | 22 ++++++++++++ .../complex/impl/TestComplexCopier.java | 35 ++++++++++++------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/vector/src/main/codegen/templates/ComplexCopier.java b/vector/src/main/codegen/templates/ComplexCopier.java index 6655f6c2a7..ebcc7ebfa0 100644 --- a/vector/src/main/codegen/templates/ComplexCopier.java +++ b/vector/src/main/codegen/templates/ComplexCopier.java @@ -117,6 +117,17 @@ public static void copy(FieldReader reader, FieldWriter writer) { writer.writeNull(); } break; + case FIXEDSIZEBINARY: + if (reader.isSet()) { + NullableFixedSizeBinaryHolder fixedSizeBinaryHolder = new NullableFixedSizeBinaryHolder(); + reader.read(fixedSizeBinaryHolder); + if (fixedSizeBinaryHolder.isSet == 1) { + writer.writeFixedSizeBinary(fixedSizeBinaryHolder.buffer); + } + } else { + writer.writeNull(); + } + break; <#list vv.types as type><#list type.minor as minor><#assign name = minor.class?cap_first /> <#assign fields = minor.fields!type.fields /> <#assign uncappedName = name?uncap_first/> @@ -160,6 +171,13 @@ private static FieldWriter getStructWriterForReader(FieldReader reader, StructWr + case FIXEDSIZEBINARY: + if (reader.getField().getType() instanceof ArrowType.FixedSizeBinary) { + ArrowType.FixedSizeBinary type = (ArrowType.FixedSizeBinary) reader.getField().getType(); + return (FieldWriter) writer.fixedSizeBinary(name, type.getByteWidth()); + } else { + return (FieldWriter) writer.fixedSizeBinary(name); + } case STRUCT: return (FieldWriter) writer.struct(name); case FIXED_SIZE_LIST: @@ -187,6 +205,8 @@ private static FieldWriter getListWriterForReader(FieldReader reader, ListWriter return (FieldWriter) writer.<#if name == "Int">integer<#else>${uncappedName}(); + case FIXEDSIZEBINARY: + return (FieldWriter) writer.fixedSizeBinary(); case STRUCT: return (FieldWriter) writer.struct(); case FIXED_SIZE_LIST: @@ -214,6 +234,8 @@ private static FieldWriter getMapWriterForReader(FieldReader reader, MapWriter w return (FieldWriter) writer.<#if name == "Int">integer<#else>${uncappedName}(); + case FIXEDSIZEBINARY: + return (FieldWriter) writer.fixedSizeBinary(); case STRUCT: return (FieldWriter) writer.struct(); case FIXED_SIZE_LIST: diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java index 9bff2a534f..785d29b7a8 100644 --- a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java +++ b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java @@ -16,12 +16,14 @@ */ package org.apache.arrow.vector.complex.impl; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; +import java.util.List; import java.util.UUID; - import org.apache.arrow.memory.ArrowBuf; import org.apache.arrow.memory.BufferAllocator; import org.apache.arrow.memory.RootAllocator; @@ -218,6 +220,7 @@ public void testCopyListOfFixedSizeBinary() { ArrowBuf buf = allocator.buffer(byteWidth)) { from.addOrGetVector(FieldType.nullable(new ArrowType.FixedSizeBinary(byteWidth))); + to.addOrGetVector(FieldType.nullable(new ArrowType.FixedSizeBinary(byteWidth))); UnionListWriter listWriter = from.getWriter(); listWriter.allocate(); @@ -240,20 +243,26 @@ public void testCopyListOfFixedSizeBinary() { } from.setValueCount(COUNT); - // copy values — this currently throws UnsupportedOperationException: FIXEDSIZEBINARY FieldReader in = from.getReader(); FieldWriter out = to.getWriter(); - UnsupportedOperationException e = - assertThrows( - UnsupportedOperationException.class, - () -> { - for (int i = 0; i < COUNT; i++) { - in.setPosition(i); - out.setPosition(i); - ComplexCopier.copy(in, out); - } - }); - assertTrue(e.getMessage().contains("FIXEDSIZEBINARY")); + for (int i = 0; i < COUNT; i++) { + in.setPosition(i); + out.setPosition(i); + ComplexCopier.copy(in, out); + } + to.setValueCount(COUNT); + + for (int i = 0; i < COUNT; i++) { + @SuppressWarnings("unchecked") + List expected = (List) from.getObject(i); + @SuppressWarnings("unchecked") + List actual = (List) to.getObject(i); + + assertEquals(expected.size(), actual.size()); + for (int j = 0; j < expected.size(); j++) { + assertArrayEquals(expected.get(j), actual.get(j)); + } + } } } From 383c8088fedad395108a1419cf10c58580bb0c4c Mon Sep 17 00:00:00 2001 From: Mariamawit Berta Date: Sun, 2 Aug 2026 20:52:29 -0700 Subject: [PATCH 3/3] GH-559: [Java] Add test coverage for FixedSizeBinary in Struct --- .../complex/impl/TestComplexCopier.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java index 785d29b7a8..9e6bb0b1a2 100644 --- a/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java +++ b/vector/src/test/java/org/apache/arrow/vector/complex/impl/TestComplexCopier.java @@ -764,6 +764,43 @@ public void testCopyStructVector() { } } + @Test + public void testCopyStructOfFixedSizeBinary() { + final int byteWidth = 4; + try (final StructVector from = StructVector.empty("v", allocator); + final StructVector to = StructVector.empty("v", allocator); + ArrowBuf buf = allocator.buffer(byteWidth)) { + from.allocateNewSafe(); + NullableStructWriter structWriter = from.getWriter(); + + FixedSizeBinaryHolder holder = new FixedSizeBinaryHolder(); + holder.byteWidth = byteWidth; + holder.buffer = buf; + + for (int i = 0; i < COUNT; i++) { + structWriter.setPosition(i); + structWriter.start(); + buf.setBytes(0, new byte[] {(byte) i, (byte) (i + 1), (byte) (i + 2), (byte) (i + 3)}); + structWriter.fixedSizeBinary("fsb", byteWidth).write(holder); + structWriter.end(); + } + from.setValueCount(COUNT); + + // copy values + FieldReader in = from.getReader(); + FieldWriter out = to.getWriter(); + for (int i = 0; i < COUNT; i++) { + in.setPosition(i); + out.setPosition(i); + ComplexCopier.copy(in, out); + } + to.setValueCount(COUNT); + + // validate equals + assertTrue(VectorEqualsVisitor.vectorEquals(from, to)); + } + } + @Test public void testCopyDecimalVectorWrongScale() { try (FixedSizeListVector from = FixedSizeListVector.empty("v", 3, allocator);