Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 52 additions & 14 deletions be/src/format_v2/parquet/parquet_statistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <parquet/types.h>

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <exception>
Expand All @@ -34,6 +35,7 @@
#include <optional>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -118,17 +120,29 @@ bool set_decoded_field(const ParquetColumnSchema& column_schema, DecodedValueKin
return read_decoded_field(column_schema, view, field, timezone).ok();
}

template <typename NativeType>
bool valid_min_max(const NativeType& min_value, const NativeType& max_value) {
if constexpr (std::is_floating_point_v<NativeType>) {
// Parquet requires readers to ignore min/max statistics if either bound is NaN.
return !std::isnan(min_value) && !std::isnan(max_value);
}
return true;
}

template <typename ParquetDType>
bool set_decoded_min_max(const std::shared_ptr<::parquet::Statistics>& statistics,
const ParquetColumnSchema& column_schema, DecodedValueKind value_kind,
ParquetColumnStatistics* column_statistics,
const cctz::time_zone* timezone) {
auto typed_statistics =
std::static_pointer_cast<::parquet::TypedStatistics<ParquetDType>>(statistics);
if (!set_decoded_field(column_schema, value_kind, typed_statistics->min(),
&column_statistics->min_value, timezone) ||
!set_decoded_field(column_schema, value_kind, typed_statistics->max(),
&column_statistics->max_value, timezone)) {
const auto& min_value = typed_statistics->min();
const auto& max_value = typed_statistics->max();
if (!valid_min_max(min_value, max_value) ||
!set_decoded_field(column_schema, value_kind, min_value, &column_statistics->min_value,
Comment thread
Gabriel39 marked this conversation as resolved.
timezone) ||
!set_decoded_field(column_schema, value_kind, max_value, &column_statistics->max_value,
timezone)) {
return false;
}
return true;
Expand Down Expand Up @@ -666,7 +680,11 @@ std::shared_ptr<segment_v2::ZoneMap> make_zonemap_from_statistics(
return std::make_shared<segment_v2::ZoneMap>(std::move(zone_map));
}
if (!statistics.has_min_max) {
return nullptr;
// Null counts remain trustworthy when min/max decoding fails (for example, because a
// floating-point bound is NaN). pass_all prevents range pruning without discarding the
// has_null/has_not_null flags needed by IS NULL and IS NOT NULL predicates.
zone_map.pass_all = true;
return std::make_shared<segment_v2::ZoneMap>(std::move(zone_map));
}
zone_map.min_value = statistics.min_value;
zone_map.max_value = statistics.max_value;
Expand All @@ -693,6 +711,11 @@ void accumulate_zonemap_stats(const ZoneMapEvalContext& ctx, ParquetPruningStats

} // namespace

std::shared_ptr<segment_v2::ZoneMap> ParquetStatisticsUtils::MakeZoneMap(
const ParquetColumnStatistics& statistics) {
return make_zonemap_from_statistics(statistics);
}

ParquetColumnStatistics ParquetStatisticsUtils::TransformColumnStatistics(
const ParquetColumnSchema& column_schema,
const std::shared_ptr<::parquet::Statistics>& statistics, const cctz::time_zone* timezone) {
Expand Down Expand Up @@ -861,8 +884,8 @@ bool check_statistics(const ::parquet::RowGroupMetaData& row_group,
DCHECK_LT(column_schema->leaf_column_id, row_group.num_columns());
auto column_chunk = row_group.ColumnChunk(column_schema->leaf_column_id);
if (column_chunk != nullptr) {
zone_map =
make_zonemap_from_statistics(ParquetStatisticsUtils::TransformColumnStatistics(
zone_map = ParquetStatisticsUtils::MakeZoneMap(
ParquetStatisticsUtils::TransformColumnStatistics(
*column_schema, column_chunk->statistics(), timezone));
}
add_slot_zonemap(&ctx, slot_index, column_schema->type, std::move(zone_map));
Expand Down Expand Up @@ -969,10 +992,18 @@ bool set_page_decoded_min_max(const std::shared_ptr<::parquet::ColumnIndex>& col
page_idx >= typed_index->max_values().size()) {
return false;
}
if (!set_decoded_field(column_schema, value_kind, typed_index->min_values()[page_idx],
&page_statistics->min_value, timezone) ||
!set_decoded_field(column_schema, value_kind, typed_index->max_values()[page_idx],
&page_statistics->max_value, timezone)) {
const auto& min_value = typed_index->min_values()[page_idx];
const auto& max_value = typed_index->max_values()[page_idx];
if (!valid_min_max(min_value, max_value)) {
// A NaN invalidates only this page's bounds, not the ColumnIndex itself. Keep the page
// conservatively by returning usable null-count statistics with has_min_max=false, while
// allowing later pages with finite bounds to remain eligible for pruning.
return true;
}
if (!set_decoded_field(column_schema, value_kind, min_value, &page_statistics->min_value,
timezone) ||
!set_decoded_field(column_schema, value_kind, max_value, &page_statistics->max_value,
timezone)) {
return false;
}
page_statistics->has_min_max = true;
Expand Down Expand Up @@ -1206,15 +1237,15 @@ bool select_ranges_for_expr_zonemap(
const auto page_count = offset_index->page_locations().size();
for (size_t page_idx = 0; page_idx < page_count; ++page_idx) {
ParquetColumnStatistics page_statistics;
if (!build_page_statistics(column_index, *column_schema, page_idx, &page_statistics,
timezone)) {
if (!ParquetStatisticsUtils::TransformColumnIndexStatistics(
column_index, *column_schema, page_idx, &page_statistics, timezone)) {
ranges->clear();
return false;
}

ZoneMapEvalContext ctx;
add_slot_zonemap(&ctx, slot_index, column_schema->type,
make_zonemap_from_statistics(page_statistics));
ParquetStatisticsUtils::MakeZoneMap(page_statistics));
const auto result = VExprContext::evaluate_zonemap_filter(conjuncts, ctx);
page_stats.merge_page_eval_stats(ctx.stats);
if (result == ZoneMapFilterResult::kNoMatch) {
Expand Down Expand Up @@ -1356,6 +1387,13 @@ void build_page_skip_plans(const std::shared_ptr<::parquet::RowGroupPageIndexRea

} // namespace

bool ParquetStatisticsUtils::TransformColumnIndexStatistics(
const std::shared_ptr<::parquet::ColumnIndex>& column_index,
const ParquetColumnSchema& column_schema, size_t page_idx,
ParquetColumnStatistics* page_statistics, const cctz::time_zone* timezone) {
return build_page_statistics(column_index, column_schema, page_idx, page_statistics, timezone);
}

Status select_row_group_ranges_by_page_index(
::parquet::ParquetFileReader* file_reader,
const std::vector<std::unique_ptr<ParquetColumnSchema>>& file_schema,
Expand Down
13 changes: 13 additions & 0 deletions be/src/format_v2/parquet/parquet_statistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#pragma once

#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
Expand All @@ -30,6 +31,7 @@

namespace parquet {
class BloomFilter;
class ColumnIndex;
class FileMetaData;
class ParquetFileReader;
class Statistics;
Expand All @@ -41,6 +43,9 @@ class time_zone;

namespace doris {
class RuntimeState;
namespace segment_v2 {
struct ZoneMap;
} // namespace segment_v2
} // namespace doris

namespace doris::format::parquet {
Expand Down Expand Up @@ -119,11 +124,19 @@ struct ParquetColumnStatistics {
// -> bloom filter(evaluate_bloom_filter)
// ============================================================================
struct ParquetStatisticsUtils {
static std::shared_ptr<segment_v2::ZoneMap> MakeZoneMap(
const ParquetColumnStatistics& statistics);

static ParquetColumnStatistics TransformColumnStatistics(
const ParquetColumnSchema& column_schema,
const std::shared_ptr<::parquet::Statistics>& statistics,
const cctz::time_zone* timezone = nullptr);

static bool TransformColumnIndexStatistics(
const std::shared_ptr<::parquet::ColumnIndex>& column_index,
const ParquetColumnSchema& column_schema, size_t page_idx,
ParquetColumnStatistics* page_statistics, const cctz::time_zone* timezone = nullptr);

static bool BloomFilterExcludes(const ParquetColumnSchema& column_schema, int slot_index,
const VExprContextSPtrs& conjuncts,
const ::parquet::BloomFilter& bloom_filter);
Expand Down
Loading
Loading