diff --git a/parquet-column/src/test/java/org/apache/parquet/column/values/dictionary/TestDictionary.java b/parquet-column/src/test/java/org/apache/parquet/column/values/dictionary/TestDictionary.java index 13033404ce..929d95f2a9 100644 --- a/parquet-column/src/test/java/org/apache/parquet/column/values/dictionary/TestDictionary.java +++ b/parquet-column/src/test/java/org/apache/parquet/column/values/dictionary/TestDictionary.java @@ -45,6 +45,7 @@ import org.apache.parquet.column.values.dictionary.DictionaryValuesWriter.PlainFloatDictionaryValuesWriter; import org.apache.parquet.column.values.dictionary.DictionaryValuesWriter.PlainIntegerDictionaryValuesWriter; import org.apache.parquet.column.values.dictionary.DictionaryValuesWriter.PlainLongDictionaryValuesWriter; +import org.apache.parquet.column.values.dictionary.PlainValuesDictionary.PlainBinaryDictionary; import org.apache.parquet.column.values.dictionary.PlainValuesDictionary.PlainBooleanDictionary; import org.apache.parquet.column.values.fallback.FallbackValuesWriter; import org.apache.parquet.column.values.plain.BinaryPlainValuesReader; @@ -803,6 +804,17 @@ public void testZeroValues() throws IOException { } } + @Test + public void testDictionaryPageCopyDoesNotAliasSourceBytes() throws IOException { + byte[] source = {1, 0, 0, 0, 'a'}; + DictionaryPage copied = new DictionaryPage(BytesInput.from(source), 1, PLAIN).copy(); + + source[4] = 'b'; + + PlainBinaryDictionary dictionary = new PlainBinaryDictionary(copied); + assertThat(dictionary.decodeToBinary(0).toStringUsingUTF8()).isEqualTo("a"); + } + @Test public void testBooleanDictionary() throws IOException { // Create a dictionary page with boolean values (false, true) diff --git a/parquet-common/src/main/java/org/apache/parquet/bytes/BytesInput.java b/parquet-common/src/main/java/org/apache/parquet/bytes/BytesInput.java index ca139d9243..89c1bca4d8 100644 --- a/parquet-common/src/main/java/org/apache/parquet/bytes/BytesInput.java +++ b/parquet-common/src/main/java/org/apache/parquet/bytes/BytesInput.java @@ -690,18 +690,9 @@ public ByteBuffer toByteBuffer() throws IOException { return java.nio.ByteBuffer.wrap(in, offset, length); } - /** - * Zero-copy override: returns the backing array directly when fully used, - * skipping the base-class BAOS allocation + copy on every decompressor call. - * Returning the mutable array is safe — the base class already exposes a - * mutable {@code BAOS.getBuf()}. - */ @SuppressWarnings("deprecation") @Override public byte[] toByteArray() { - if (offset == 0 && length == in.length) { - return in; - } return Arrays.copyOfRange(in, offset, offset + length); } diff --git a/parquet-common/src/test/java/org/apache/parquet/bytes/TestBytesInput.java b/parquet-common/src/test/java/org/apache/parquet/bytes/TestBytesInput.java index a80c874fa2..2eb8aac413 100644 --- a/parquet-common/src/test/java/org/apache/parquet/bytes/TestBytesInput.java +++ b/parquet-common/src/test/java/org/apache/parquet/bytes/TestBytesInput.java @@ -123,16 +123,16 @@ public void testFromByteArray(ByteBufferAllocator innerAllocator) throws IOExcep @ParameterizedTest(name = "{0}") @MethodSource("parameters") - public void testFromByteArrayToByteArrayZeroCopy(ByteBufferAllocator innerAllocator) throws IOException { + public void testFromByteArrayToByteArrayCopiesFullArray(ByteBufferAllocator innerAllocator) throws IOException { initAllocator(innerAllocator); - // Full array (offset=0, length=array.length): toByteArray() returns the backing array directly byte[] data = new byte[1000]; RANDOM.nextBytes(data); BytesInput bi = BytesInput.from(data, 0, data.length); byte[] result = bi.toByteArray(); assertThat(result) - .as("toByteArray() should return the backing array when offset=0 and length=full") - .isSameAs(data); + .as("toByteArray() should materialize a copy when offset=0 and length=full") + .isEqualTo(data) + .isNotSameAs(data); } @ParameterizedTest(name = "{0}")