From 34a9a7ca7cca0e1bd0cb3d9a29b5758d99504935 Mon Sep 17 00:00:00 2001 From: Nilanjan Chatterjee Date: Sun, 11 Oct 2026 13:58:22 -0500 Subject: [PATCH] GH-49683: [C++][Parquet] Fix def level for fixed_size_list with list_size 0 The writer used the list's full definition level for empty fixed-size lists, so each zero-sized list was read back as a list with one null element. Use max_def_level - 1, as for variable-size lists. Co-Authored-By: Claude Opus 5.5 --- cpp/src/parquet/arrow/arrow_reader_writer_test.cc | 9 +++++++++ cpp/src/parquet/arrow/path_internal.cc | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cpp/src/parquet/arrow/arrow_reader_writer_test.cc b/cpp/src/parquet/arrow/arrow_reader_writer_test.cc index d858962d4728..4dc20d8c716e 100644 --- a/cpp/src/parquet/arrow/arrow_reader_writer_test.cc +++ b/cpp/src/parquet/arrow/arrow_reader_writer_test.cc @@ -3571,6 +3571,15 @@ static const std::vector kFixedSizeListTestCases = { {"root": [[1, 2], null, [3, 4]]}, {"root": null}, {"root": [null, [5, 6]]}, + {"root": []}])"}, + // GH-49683: zero-sized lists + {.type = ::arrow::fixed_size_list(::arrow::float32(), /*list_size=*/0), .json = R"([ + {"root": []}, + {"root": []}, + {"root": []}])"}, + {.type = ::arrow::fixed_size_list(::arrow::float32(), /*list_size=*/0), .json = R"([ + {"root": []}, + {"root": null}, {"root": []}])"}}; TEST_P(TestFixedSizeListRoundTrip, RoundTrip) { diff --git a/cpp/src/parquet/arrow/path_internal.cc b/cpp/src/parquet/arrow/path_internal.cc index c626dcbf48b6..d61a69d2e160 100644 --- a/cpp/src/parquet/arrow/path_internal.cc +++ b/cpp/src/parquet/arrow/path_internal.cc @@ -846,8 +846,10 @@ class PathBuilder { // well. info_.max_def_level++; info_.max_rep_level++; - info_.path.emplace_back(FixedSizeListNode(FixedSizedRangeSelector{list_size}, - info_.max_rep_level, info_.max_def_level)); + // Lists are empty only when list_size == 0 (GH-49683). + info_.path.emplace_back( + FixedSizeListNode(FixedSizedRangeSelector{list_size}, info_.max_rep_level, + /*def_level_if_empty=*/info_.max_def_level - 1)); nullable_in_parent_ = array.list_type()->value_field()->nullable(); if (array.offset() > 0) { return VisitInline(*array.values()->Slice(array.value_offset(0)));