github-actions[bot] commented on code in PR #66056:
URL: https://github.com/apache/doris/pull/66056#discussion_r3656209102
##########
be/src/format_v2/parquet/reader/native/column_chunk_reader.cpp:
##########
@@ -1644,8 +1644,10 @@ bool ColumnChunkReader<IN_COLLECTION,
OFFSET_INDEX>::can_filter_fixed_width_valu
}
const auto primitive_type =
remove_nullable(_field_schema->data_type)->get_primitive_type();
const bool has_identity_width =
- (_metadata.type == tparquet::Type::INT32 && primitive_type ==
TYPE_INT) ||
- (_metadata.type == tparquet::Type::INT64 && primitive_type ==
TYPE_BIGINT) ||
+ (_metadata.type == tparquet::Type::INT32 &&
+ (primitive_type == TYPE_INT || primitive_type == TYPE_DECIMAL32))
||
Review Comment:
[P1] Preserve decimal conversion before raw filtering
These new DECIMAL32/64 branches treat matching byte width as identity, but
the ordinary decimal SerDe still has required work: Parquet DECIMAL(p<=9/18)
uses INT32/INT64 carriers whose full 10/19-digit domains do not fit the
declared precision. `DecimalParquetConsumer::append_integers()` therefore
validates every carrier and either raises `DataQualityError` or marks a
nullable value NULL. The raw path can instead reject the carrier before SerDe
(so a strict predicate-only scan silently succeeds), or copy a
surviving/Bloom-false-positive carrier with `insert_many_raw_data()` as a
non-NULL decimal. For example, an INT32 carrier `999999` in DECIMAL(5,0) no
longer has the ordinary error behavior. Please keep these decimals on the SerDe
path, or perform the same precision/scale and strict/non-strict conversion
before treating the raw bitmap/projection as final; cover malformed carriers in
both predicate-only and projected tests.
##########
be/src/exprs/vdirect_in_predicate.h:
##########
@@ -98,6 +98,54 @@ class VDirectInPredicate final : public VExpr {
std::dynamic_pointer_cast<VSlotRef>(get_child(0)) != nullptr;
}
+ ZoneMapFilterResult evaluate_dictionary_filter(
+ const DictionaryEvalContext& ctx) const override {
+ return expr_zonemap::eval_in_dictionary(ctx, get_child(0), false,
_seg_filter_values);
Review Comment:
[P1] Avoid quadratic string-IN dictionary probing
This newly enables string Direct-IN runtime filters to call
`eval_in_dictionary()`, which iterates every materialized IN value and uses
`dictionary_contains()` to linearly scan the supplied dictionary. Parquet first
does that against the full row-group dictionary, then its generic ID-bitmap
builder supplies one dictionary entry at a time and repeats the entire IN list
for every ID because the typed string kernel recognizes only
`VectorizedFnCall`, not this runtime-filter wrapper. That makes setup
O(dictionary entries * RF values): a 65K-entry dictionary and 1K-value filter
already perform about 65 million `Field` comparisons per stage, while the
configured IN limit is much larger and the underlying `HybridSet` already
supports hash probes. Please add a wrapper-aware string kernel that probes the
HybridSet once per dictionary entry (or disable this delegation for strings),
with a large-cardinality regression test/benchmark.
##########
be/src/exprs/runtime_filter_expr.cpp:
##########
@@ -236,6 +236,37 @@ bool RuntimeFilterExpr::can_evaluate_zonemap_filter()
const {
return _impl->can_evaluate_zonemap_filter();
}
+bool RuntimeFilterExpr::can_execute_on_raw_fixed_values(const DataTypePtr&
data_type,
+ int column_id) const {
+ // Raw and dictionary streams omit NULL payloads and currently map NULL
rows to false. A
+ // null-aware RF must therefore stay on execute_filter(), which restores
its NULL semantics.
+ return !_null_aware && _impl->can_execute_on_raw_fixed_values(data_type,
column_id);
+}
+
+Status RuntimeFilterExpr::execute_on_raw_fixed_values(const uint8_t* values,
size_t num_values,
+ size_t value_width,
+ const DataTypePtr&
data_type, int column_id,
+ uint8_t* matches) const {
+ if (!can_execute_on_raw_fixed_values(data_type, column_id)) {
+ return Status::NotSupported("Runtime filter {} cannot evaluate raw
fixed-width values",
+ _filter_id);
+ }
+ return _impl->execute_on_raw_fixed_values(values, num_values, value_width,
data_type, column_id,
Review Comment:
[P2] Preserve runtime-filter sampling and accounting
The raw delegate here (and the dictionary delegate below) calls `_impl`
directly, bypassing `RuntimeFilterExpr::execute_filter()`, which is the only
place that consults `maybe_always_true_can_ignore()`, updates
`RuntimeFilterSelectivity`, and increments the per-filter
input/rejected/always-true counters. Parquet treats these bitmaps as final and
removes the exact residual, so rows rejected early never reach the wrapper. A
six-row RF that removes three can consequently be profiled/sampled as three
inputs and zero rejects; ineffective filters also cannot age into the ignore
state, and aggregate Parquet counters cannot identify which RF did the work.
Please plumb the owning wrapper's accounting/ignore state into bitmap
application (or retain an accounting-only residual), and assert the RF-specific
counters in the new scan tests.
##########
be/src/format_v2/parquet/reader/native/decoder.h:
##########
@@ -246,6 +256,57 @@ class BaseDictDecoder : public Decoder {
size_t active_scratch_bytes() const override { return _skip_indices.size()
* sizeof(uint32_t); }
protected:
+ static bool _is_fragmented_selection(const ParquetSelection& selection) {
+ constexpr size_t MIN_FRAGMENTED_RANGES = 8;
+ constexpr size_t MAX_AVERAGE_RANGE_VALUES = 4;
+ constexpr size_t MAX_DECODE_EXPANSION = 8;
+ return selection.ranges.size() >= MIN_FRAGMENTED_RANGES &&
selection.selected_values != 0 &&
+ selection.total_values / selection.selected_values <=
MAX_DECODE_EXPANSION &&
+ selection.selected_values <= selection.ranges.size() *
MAX_AVERAGE_RANGE_VALUES;
+ }
+
+ Status _decode_fragmented_selection(const ParquetSelection& selection,
+ size_t num_dictionary_values) {
+ // Decode and validate the page batch once when predicate survivors
alternate in tiny runs.
+ // Walking each range separately turns one RLE batch into millions of
decoder calls for
+ // low-cardinality predicates such as TPC-DS quantity buckets.
+ _skip_indices.resize(selection.total_values);
Review Comment:
[P2] Clear fragmented scratch's active size after compaction
This resizes `_skip_indices` to the full physical batch and compacts
survivors into its prefix, but never reduces the vector's logical size. A valid
65,535-parent nested batch alternating four selected leaf IDs with 31 rejected
IDs has 32,768 ranges, 131,072 selected IDs, and 1,146,849 total IDs (4,587,396
bytes); it passes the new gates because integer `total / selected == 8` and
`selected == 4 * ranges`. If later dictionary pages are small all-repeated RLE
runs, neither `_decode_dictionary_values()` nor `set_data()` resizes this
vector. `active_scratch_bytes()` therefore remains above the 4 MiB limit even
though those IDs are dead, and `release_batch_scratch()` resets the idle
counter forever instead of releasing the capacity. Please reset/shrink the
logical active size after the fragmented result is consumed, and add a
high-water test that follows such an outlier with repeated-run idle batches
through the release hysteresis.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]