From 175d3de08171456bb0b91d93d90e4c317defea52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=AE=B6=E8=B4=9D?= <41840745+Sunny-Island@users.noreply.github.com> Date: Mon, 5 Oct 2026 01:11:33 +0800 Subject: [PATCH 1/2] GH-51673: [C++][Parquet] Skip missing min/max in metadata printer --- cpp/src/parquet/printer.cc | 23 +++++++++++++++++------ cpp/src/parquet/reader_test.cc | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cpp/src/parquet/printer.cc b/cpp/src/parquet/printer.cc index cf4decb32fd2..b4c771bfc992 100644 --- a/cpp/src/parquet/printer.cc +++ b/cpp/src/parquet/printer.cc @@ -167,7 +167,6 @@ void ParquetFilePrinter::DebugPrint(std::ostream& stream, std::list selecte } stream << " Values: " << column_chunk->num_values(); if (column_chunk->is_stats_set()) { - std::string min = stats->min(), max = stats->max(); std::string max_exact = stats->is_max_value_exact.has_value() ? (stats->is_max_value_exact.value() ? "true" : "false") @@ -177,11 +176,23 @@ void ParquetFilePrinter::DebugPrint(std::ostream& stream, std::list selecte ? (stats->is_min_value_exact.value() ? "true" : "false") : "unknown"; stream << ", Null Values: " << stats->null_count - << ", Distinct Values: " << stats->distinct_count << std::endl - << " Max (exact: " << max_exact << "): " - << FormatStatValue(descr->physical_type(), max, descr->logical_type()) - << ", Min (exact: " << min_exact << "): " - << FormatStatValue(descr->physical_type(), min, descr->logical_type()); + << ", Distinct Values: " << stats->distinct_count; + if (stats->has_max || stats->has_min) { + stream << std::endl << " "; + if (stats->has_max) { + stream << "Max (exact: " << max_exact << "): " + << FormatStatValue(descr->physical_type(), stats->max(), + descr->logical_type()); + } + if (stats->has_min) { + if (stats->has_max) { + stream << ", "; + } + stream << "Min (exact: " << min_exact << "): " + << FormatStatValue(descr->physical_type(), stats->min(), + descr->logical_type()); + } + } } else { stream << " Statistics Not Set"; } diff --git a/cpp/src/parquet/reader_test.cc b/cpp/src/parquet/reader_test.cc index b5487fa46899..96bbc6766191 100644 --- a/cpp/src/parquet/reader_test.cc +++ b/cpp/src/parquet/reader_test.cc @@ -568,6 +568,38 @@ TEST_F(TestAllTypesPlain, DebugPrintWorks) { ASSERT_GT(result.size(), 0); } +TEST(TestDebugPrintWithMemoryFile, AllNullDecimalStatistics) { + auto schema = std::static_pointer_cast(GroupNode::Make( + "schema", Repetition::REQUIRED, + {PrimitiveNode::Make("decimal", Repetition::OPTIONAL, + LogicalType::Decimal(10, 3), Type::FIXED_LEN_BYTE_ARRAY, 5)})); + ASSERT_OK_AND_ASSIGN(auto output, ::arrow::io::BufferOutputStream::Create()); + auto writer = ParquetFileWriter::Open(output, schema); + auto row_group = writer->AppendRowGroup(); + auto column = static_cast(row_group->NextColumn()); + const std::vector definition_levels(3, 0); + column->WriteBatch(definition_levels.size(), definition_levels.data(), nullptr, + nullptr); + row_group->Close(); + writer->Close(); + + ASSERT_OK_AND_ASSIGN(auto buffer, output->Finish()); + auto reader = + ParquetFileReader::Open(std::make_shared<::arrow::io::BufferReader>(buffer)); + auto stats = reader->metadata()->RowGroup(0)->ColumnChunk(0)->encoded_statistics(); + ASSERT_NE(stats, nullptr); + ASSERT_TRUE(stats->has_null_count); + ASSERT_FALSE(stats->has_min); + ASSERT_FALSE(stats->has_max); + + std::stringstream stream; + ParquetFilePrinter(reader.get()).DebugPrint(stream, {}, false); + const std::string result = stream.str(); + EXPECT_THAT(result, testing::HasSubstr("Values: 3, Null Values: 3")); + EXPECT_THAT(result, testing::Not(testing::HasSubstr("Max (exact:"))); + EXPECT_THAT(result, testing::Not(testing::HasSubstr("Min (exact:"))); +} + TEST_F(TestAllTypesPlain, ColumnSelection) { std::stringstream ss; From 5e98701d625647f3e46bc764d16dc79f1511af97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=AE=B6=E8=B4=9D?= <970058803@qq.com> Date: Tue, 6 Oct 2026 16:43:31 +0800 Subject: [PATCH 2/2] GH-51673: Address metadata printer review comments --- cpp/src/parquet/printer.cc | 16 ++++++++-------- cpp/src/parquet/reader_test.cc | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cpp/src/parquet/printer.cc b/cpp/src/parquet/printer.cc index b4c771bfc992..996ae0e0a9e5 100644 --- a/cpp/src/parquet/printer.cc +++ b/cpp/src/parquet/printer.cc @@ -167,24 +167,24 @@ void ParquetFilePrinter::DebugPrint(std::ostream& stream, std::list selecte } stream << " Values: " << column_chunk->num_values(); if (column_chunk->is_stats_set()) { - std::string max_exact = - stats->is_max_value_exact.has_value() - ? (stats->is_max_value_exact.value() ? "true" : "false") - : "unknown"; - std::string min_exact = - stats->is_min_value_exact.has_value() - ? (stats->is_min_value_exact.value() ? "true" : "false") - : "unknown"; stream << ", Null Values: " << stats->null_count << ", Distinct Values: " << stats->distinct_count; if (stats->has_max || stats->has_min) { stream << std::endl << " "; if (stats->has_max) { + std::string max_exact = + stats->is_max_value_exact.has_value() + ? (stats->is_max_value_exact.value() ? "true" : "false") + : "unknown"; stream << "Max (exact: " << max_exact << "): " << FormatStatValue(descr->physical_type(), stats->max(), descr->logical_type()); } if (stats->has_min) { + std::string min_exact = + stats->is_min_value_exact.has_value() + ? (stats->is_min_value_exact.value() ? "true" : "false") + : "unknown"; if (stats->has_max) { stream << ", "; } diff --git a/cpp/src/parquet/reader_test.cc b/cpp/src/parquet/reader_test.cc index 96bbc6766191..fd17e3935de2 100644 --- a/cpp/src/parquet/reader_test.cc +++ b/cpp/src/parquet/reader_test.cc @@ -568,11 +568,13 @@ TEST_F(TestAllTypesPlain, DebugPrintWorks) { ASSERT_GT(result.size(), 0); } +// GH-51673: An all-null DECIMAL column has a null count but no min/max. +// The printer must not try to decode missing min/max values. TEST(TestDebugPrintWithMemoryFile, AllNullDecimalStatistics) { auto schema = std::static_pointer_cast(GroupNode::Make( "schema", Repetition::REQUIRED, - {PrimitiveNode::Make("decimal", Repetition::OPTIONAL, - LogicalType::Decimal(10, 3), Type::FIXED_LEN_BYTE_ARRAY, 5)})); + {PrimitiveNode::Make("decimal", Repetition::OPTIONAL, LogicalType::Decimal(10, 3), + Type::FIXED_LEN_BYTE_ARRAY, 5)})); ASSERT_OK_AND_ASSIGN(auto output, ::arrow::io::BufferOutputStream::Create()); auto writer = ParquetFileWriter::Open(output, schema); auto row_group = writer->AppendRowGroup();