diff --git a/cpp/src/parquet/arrow/arrow_statistics_test.cc b/cpp/src/parquet/arrow/arrow_statistics_test.cc index ad4496933ef4..a19303c3dc03 100644 --- a/cpp/src/parquet/arrow/arrow_statistics_test.cc +++ b/cpp/src/parquet/arrow/arrow_statistics_test.cc @@ -156,4 +156,27 @@ INSTANTIATE_TEST_SUITE_P( /*expected_min=*/"z", /*expected_max=*/"z"})); +TEST(StatisticsTest, TruncateOnlyHalfMinMax) { + // GH-43382: Tests when we only have min or max, the `HasMinMax` should be false. + std::shared_ptr<::arrow::ResizableBuffer> serialized_data = AllocateBuffer(); + auto out_stream = std::make_shared<::arrow::io::BufferOutputStream>(serialized_data); + auto schema = ::arrow::schema({::arrow::field("a", ::arrow::utf8())}); + ::parquet::WriterProperties::Builder properties_builder; + properties_builder.max_statistics_size(2); + ASSERT_OK_AND_ASSIGN( + std::unique_ptr writer, + FileWriter::Open(*schema, default_memory_pool(), out_stream, + properties_builder.build(), default_arrow_writer_properties())); + auto table = Table::Make(schema, {ArrayFromJSON(::arrow::utf8(), R"(["a", "abc"])")}); + ASSERT_OK(writer->WriteTable(*table, std::numeric_limits::max())); + ASSERT_OK(writer->Close()); + ASSERT_OK(out_stream->Close()); + + auto buffer_reader = std::make_shared<::arrow::io::BufferReader>(serialized_data); + auto parquet_reader = ParquetFileReader::Open(std::move(buffer_reader)); + std::shared_ptr metadata = parquet_reader->metadata(); + std::shared_ptr stats = metadata->RowGroup(0)->ColumnChunk(0)->statistics(); + ASSERT_FALSE(stats->HasMinMax()); +} + } // namespace parquet::arrow diff --git a/cpp/src/parquet/metadata.cc b/cpp/src/parquet/metadata.cc index 139793219df9..fe16f5b76bd0 100644 --- a/cpp/src/parquet/metadata.cc +++ b/cpp/src/parquet/metadata.cc @@ -97,7 +97,7 @@ static std::shared_ptr MakeTypedColumnStats( descr, metadata.statistics.min_value, metadata.statistics.max_value, metadata.num_values - metadata.statistics.null_count, metadata.statistics.null_count, metadata.statistics.distinct_count, - metadata.statistics.__isset.max_value || metadata.statistics.__isset.min_value, + metadata.statistics.__isset.max_value && metadata.statistics.__isset.min_value, metadata.statistics.__isset.null_count, metadata.statistics.__isset.distinct_count); } @@ -106,7 +106,7 @@ static std::shared_ptr MakeTypedColumnStats( descr, metadata.statistics.min, metadata.statistics.max, metadata.num_values - metadata.statistics.null_count, metadata.statistics.null_count, metadata.statistics.distinct_count, - metadata.statistics.__isset.max || metadata.statistics.__isset.min, + metadata.statistics.__isset.max && metadata.statistics.__isset.min, metadata.statistics.__isset.null_count, metadata.statistics.__isset.distinct_count); } diff --git a/cpp/src/parquet/statistics.h b/cpp/src/parquet/statistics.h index 0d6ea9898f7b..c5da44a7b666 100644 --- a/cpp/src/parquet/statistics.h +++ b/cpp/src/parquet/statistics.h @@ -246,7 +246,7 @@ class PARQUET_EXPORT Statistics { /// \brief The number of non-null values in the column virtual int64_t num_values() const = 0; - /// \brief Return true if the min and max statistics are set. Obtain + /// \brief Return true if both min and max statistics are set. Obtain /// with TypedStatistics::min and max virtual bool HasMinMax() const = 0; diff --git a/cpp/src/parquet/statistics_test.cc b/cpp/src/parquet/statistics_test.cc index cb2e6455abfa..dad414ac89b4 100644 --- a/cpp/src/parquet/statistics_test.cc +++ b/cpp/src/parquet/statistics_test.cc @@ -1602,5 +1602,30 @@ TEST(TestEncodedStatistics, CopySafe) { EXPECT_EQ("abc", encoded_statistics.max()); } +TEST(TestEncodedStatistics, ApplyStatSizeLimits) { + EncodedStatistics encoded_statistics; + encoded_statistics.set_min("a"); + encoded_statistics.has_min = true; + + encoded_statistics.set_max("abc"); + encoded_statistics.has_max = true; + + encoded_statistics.ApplyStatSizeLimits(2); + + ASSERT_TRUE(encoded_statistics.has_min); + ASSERT_EQ("a", encoded_statistics.min()); + ASSERT_FALSE(encoded_statistics.has_max); + + NodePtr node = + PrimitiveNode::Make("StringColumn", Repetition::REQUIRED, Type::BYTE_ARRAY); + ColumnDescriptor descr(node, 0, 0); + std::shared_ptr> statistics = + std::dynamic_pointer_cast>( + Statistics::Make(&descr, &encoded_statistics, + /*num_values=*/1000)); + // GH-43382: HasMinMax should be false if one of min/max is not set. + EXPECT_FALSE(statistics->HasMinMax()); +} + } // namespace test } // namespace parquet