From b1c13e25f1af7fb120864359a36f41fc7e9dc590 Mon Sep 17 00:00:00 2001 From: "s.kyon" <251157463+skyon0522-ai@users.noreply.github.com> Date: Sun, 18 Oct 2026 23:07:05 +0900 Subject: [PATCH] GH-3043: Ignore V1 level encodings for omitted streams Use the existing zero reader when a column's maximum repetition or definition level is zero. Such a column has no corresponding encoded level stream, so its unused header encoding must not select a physical value reader or consume value bytes. Preserve encoding dispatch for nonzero levels and add synthetic V1 regressions for required, optional and repeated columns across two pages. Generated-by: OpenAI Codex --- .../parquet/column/impl/ColumnReaderBase.java | 9 ++- .../column/impl/TestColumnReaderImpl.java | 62 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/parquet-column/src/main/java/org/apache/parquet/column/impl/ColumnReaderBase.java b/parquet-column/src/main/java/org/apache/parquet/column/impl/ColumnReaderBase.java index 2b3e47116c..db7c574c60 100644 --- a/parquet-column/src/main/java/org/apache/parquet/column/impl/ColumnReaderBase.java +++ b/parquet-column/src/main/java/org/apache/parquet/column/impl/ColumnReaderBase.java @@ -42,6 +42,7 @@ import org.apache.parquet.column.values.RequiresPreviousReader; import org.apache.parquet.column.values.ValuesReader; import org.apache.parquet.column.values.rle.RunLengthBitPackingHybridDecoder; +import org.apache.parquet.column.values.rle.ZeroIntegerValuesReader; import org.apache.parquet.io.ParquetDecodingException; import org.apache.parquet.io.api.Binary; import org.apache.parquet.io.api.PrimitiveConverter; @@ -727,8 +728,12 @@ private void initDataReader(Encoding dataEncoding, ByteBufferInputStream in, int } private void readPageV1(DataPageV1 page) { - ValuesReader rlReader = page.getRlEncoding().getValuesReader(path, REPETITION_LEVEL); - ValuesReader dlReader = page.getDlEncoding().getValuesReader(path, DEFINITION_LEVEL); + ValuesReader rlReader = path.getMaxRepetitionLevel() == 0 + ? new ZeroIntegerValuesReader() + : page.getRlEncoding().getValuesReader(path, REPETITION_LEVEL); + ValuesReader dlReader = path.getMaxDefinitionLevel() == 0 + ? new ZeroIntegerValuesReader() + : page.getDlEncoding().getValuesReader(path, DEFINITION_LEVEL); this.repetitionLevelColumn = new ValuesReaderIntIterator(rlReader); this.definitionLevelColumn = new ValuesReaderIntIterator(dlReader); int valueCount = page.getValueCount(); diff --git a/parquet-column/src/test/java/org/apache/parquet/column/impl/TestColumnReaderImpl.java b/parquet-column/src/test/java/org/apache/parquet/column/impl/TestColumnReaderImpl.java index aa0e189194..4322062004 100644 --- a/parquet-column/src/test/java/org/apache/parquet/column/impl/TestColumnReaderImpl.java +++ b/parquet-column/src/test/java/org/apache/parquet/column/impl/TestColumnReaderImpl.java @@ -31,6 +31,7 @@ import org.apache.parquet.column.ColumnDescriptor; import org.apache.parquet.column.ColumnReader; import org.apache.parquet.column.Dictionary; +import org.apache.parquet.column.Encoding; import org.apache.parquet.column.ParquetProperties; import org.apache.parquet.column.ParquetProperties.WriterVersion; import org.apache.parquet.column.page.DataPage; @@ -44,6 +45,8 @@ import org.apache.parquet.schema.MessageType; import org.apache.parquet.schema.MessageTypeParser; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; public class TestColumnReaderImpl { @@ -188,6 +191,65 @@ public Dictionary decode(ColumnDescriptor path) { validateExpectedValuesAndCount(col, pageReader); } + @ParameterizedTest + @CsvSource({ + "required, PLAIN, RLE", + "required, RLE, PLAIN", + "required, PLAIN, PLAIN", + "required, RLE, RLE", + "optional, PLAIN, RLE", + "repeated, RLE, RLE" + }) + public void testV1AbsentLevelEncodings(String repetition, Encoding rlEncoding, Encoding dlEncoding) + throws Exception { + MessageType schema = MessageTypeParser.parseMessageType("message test { " + repetition + " double foo; }"); + ColumnDescriptor col = schema.getColumns().get(0); + MemPageWriter pageWriter = new MemPageWriter(); + ColumnWriterV1 writer = new ColumnWriterV1( + col, + pageWriter, + ParquetProperties.builder() + .withWriterVersion(PARQUET_1_0) + .withDictionaryEncoding(false) + .build()); + for (int i = 0; i < 4; i++) { + writer.write(12.5 + i, (i % 2) * col.getMaxRepetitionLevel(), col.getMaxDefinitionLevel()); + if (i % 2 == 1) { + writer.writePage(); + } + } + writer.finalizeColumnChunk(); + writer.close(); + assertThat(pageWriter.getPages()).hasSize(2); + List pages = new ArrayList<>(); + for (DataPage page : pageWriter.getPages()) { + DataPageV1 v1 = (DataPageV1) page; + assertThat(v1.getValueEncoding()).isEqualTo(Encoding.PLAIN); + if (col.getMaxDefinitionLevel() == 0) { + assertThat(v1.getBytes().size()).isEqualTo(2 * Double.BYTES); + } + pages.add(new DataPageV1( + v1.getBytes(), + v1.getValueCount(), + v1.getUncompressedSize(), + v1.getStatistics(), + rlEncoding, + dlEncoding, + v1.getValueEncoding())); + } + ColumnReader reader = new ColumnReaderImpl( + col, + new MemPageReader(4, pages.iterator(), null), + new PrimitiveConverter() {}, + VersionParser.parse(Version.FULL_VERSION)); + for (int i = 0; i < 4; i++) { + assertThat(reader.getCurrentRepetitionLevel()).isEqualTo((i % 2) * col.getMaxRepetitionLevel()); + assertThat(reader.getCurrentDefinitionLevel()).isEqualTo(col.getMaxDefinitionLevel()); + assertThat(reader.getDouble()).isEqualTo(12.5 + i); + reader.consume(); + } + } + @Test public void testSynchronizingLastRowV1() throws Exception { testSynchronizingReader(PARQUET_1_0, false);