github-actions[bot] commented on code in PR #66412:
URL: https://github.com/apache/doris/pull/66412#discussion_r3714400685


##########
be/src/format_v2/parquet/parquet_scan.cpp:
##########
@@ -1834,6 +2293,297 @@ Status 
ParquetScanScheduler::prepare_current_dictionary_filters(
         _current_predicate_columns.emplace(local_id, std::move(column_reader));
         update_counter_if_not_null(_scan_profile.dict_filter_columns, 1);
     }
+
+    std::unordered_map<size_t, size_t> remaining_position_occurrences;
+    for (const auto& remaining_stage : schedule.remaining_stages) {
+        for (const size_t position : remaining_stage.required_positions) {
+            ++remaining_position_occurrences[position];
+        }
+    }
+    std::unordered_set<size_t> delete_positions;
+    for (const auto& conjunct : request.delete_conjuncts) {
+        std::set<int> positions;
+        conjunct->root()->collect_slot_column_ids(positions);
+        for (const int position : positions) {
+            if (position >= 0) {
+                delete_positions.insert(cast_set<size_t>(position));
+            }
+        }
+    }
+
+    for (const auto& stage : schedule.remaining_stages) {
+        if (stage.raw_disjunction_branches.empty() ||
+            _disabled_raw_disjunctions.contains(stage.expression.get())) {
+            continue;
+        }
+        std::vector<RawDisjunctionBranchReader> branch_readers;
+        branch_readers.reserve(stage.raw_disjunction_branches.size());
+        bool usable = true;
+        for (const auto& branch : stage.raw_disjunction_branches) {
+            const auto predicate_index_it =
+                    
_predicate_indices_by_position_scratch.find(branch.position);
+            if (predicate_index_it == 
_predicate_indices_by_position_scratch.end()) {
+                usable = false;
+                break;
+            }
+            const auto& column = 
request.predicate_columns[predicate_index_it->second];
+            const auto local_id = column.column_id();
+            if (!local_id.is_valid() ||
+                local_id.value() >= static_cast<int32_t>(file_schema.size())) {
+                usable = false;
+                break;
+            }
+            const auto& column_schema = file_schema[local_id.value()];
+            DORIS_CHECK(column_schema != nullptr);
+            if (column_schema->leaf_column_id < 0 ||
+                column_schema->leaf_column_id >=
+                        static_cast<int>(row_group_metadata.columns.size())) {
+                usable = false;
+                break;
+            }
+            const auto& column_chunk = 
row_group_metadata.columns[column_schema->leaf_column_id];
+            if (!column_chunk.__isset.meta_data) {
+                usable = false;
+                break;
+            }
+            const int expression_column_id = cast_set<int>(branch.position);
+            const bool raw_eligible = 
branch.expression->can_execute_on_raw_fixed_values(
+                                              column_schema->type, 
expression_column_id) ||
+                                      
branch.expression->can_execute_on_raw_binary_values(
+                                              column_schema->type, 
expression_column_id) ||
+                                      
branch.expression->can_execute_on_null_map(
+                                              column_schema->type, 
expression_column_id);
+            if (!raw_eligible) {
+                usable = false;
+                break;
+            }
+
+            bool dictionary_eligible = false;
+            if (!branch.expression->raw_predicate_result_for_null()) {
+                dictionary_eligible = supports_row_level_dictionary_filter(
+                                              *column_schema, 
column_chunk.meta_data) &&
+                                      
(branch.expression->can_execute_on_raw_fixed_values(
+                                               column_schema->type, 
expression_column_id) ||
+                                       
branch.expression->can_execute_on_raw_binary_values(
+                                               column_schema->type, 
expression_column_id));
+            }
+            const bool projected = !request.is_predicate_only(local_id);
+            const int minimum_null_denominator = dictionary_eligible && 
!projected ? 10 : 2;
+            if ((!dictionary_eligible && projected) ||
+                !has_null_fraction_at_least(column_chunk.meta_data, 1, 
minimum_null_denominator)) {
+                // Branch readers pay off only after avoiding enough nullable 
materialization. The
+                // paired matrix establishes conservative 10% dictionary and 
50% PLAIN thresholds;
+                // projected PLAIN branches retain the residual path because 
they decode twice.
+                usable = false;
+                break;
+            }
+            const bool use_predicate_reader =
+                    request.is_predicate_only(local_id) &&
+                    remaining_position_occurrences[branch.position] == 1 &&
+                    !delete_positions.contains(branch.position) &&
+                    
!schedule.single_column_conjuncts.contains(branch.position);
+            if (dictionary_eligible) {
+                
update_counter_if_not_null(_scan_profile.dict_filter_candidate_columns, 1);
+            }
+
+            std::unique_ptr<ParquetColumnReader> column_reader;
+            if (!use_predicate_reader || dictionary_eligible) {
+                RETURN_IF_ERROR(NativeColumnReader::create(
+                        *column_schema, &column, file_context.native_file,
+                        file_context.native_metadata, row_group_idx, 
_current_selected_ranges,
+                        _current_offset_indexes, _timezone, 
file_context.native_io_ctx,
+                        _runtime_state, file_context.native_page_cache_enabled,
+                        file_context.native_page_cache_file_key, 
dictionary_eligible,
+                        _scan_profile.column_reader_profile, &column_reader));
+            }
+
+            std::optional<IColumn::Filter> dictionary_filter;
+            if (dictionary_eligible) {
+                MutableColumnPtr dictionary_values;
+                {
+                    SCOPED_TIMER(_scan_profile.dict_filter_read_dict_time);
+                    auto dictionary_result = 
column_reader->dictionary_values();

Review Comment:
   [P1] Disable raw OR when the dictionary probe fails
   
   This is an optional optimization probe, but on failure this branch is still 
installed with no `dictionary_filter`; for a predicate-only single-use column, 
it is also installed with `reader == nullptr`. A fully dictionary-encoded 
DATE/TIMESTAMP/DECIMAL column can reach this when converting one dictionary 
entry fails. Execution then reuses the ordinary reader, direct fixed-width 
filtering refuses the dictionary encoding, and line 3377 turns the refusal into 
`InternalError` instead of running the supported residual path (even if the bad 
entry is unreferenced). Mark the entire OR stage unusable when a 
dictionary-eligible branch cannot produce its bitmap, and cover referenced and 
unreferenced invalid dictionary entries. This probe-failure case is not fixed 
by the complete-encoding preflight requested in the existing mixed-encoding 
thread.



##########
be/benchmark/parquet/benchmark_parquet_reader.hpp:
##########
@@ -171,9 +203,9 @@ inline ::parquet::Encoding::type file_encoding(Encoding 
encoding) {
 }
 
 inline std::string fixture_name(const ReaderScenario& scenario) {
-    return "v2_" + to_string(scenario.encoding) + "_" + 
to_string(scenario.value_type) + "_null" +
-           std::to_string(scenario.null_percent) + "_" + 
to_string(scenario.null_pattern) + "_w" +
-           std::to_string(scenario.schema_width) + "_p" +
+    return "v4_" + to_string(scenario.operation) + "_" + 
to_string(scenario.encoding) + "_" +

Review Comment:
   [P2] Keep byte-identical operations on one fixture identity
   
   Adding `operation` to every path splits ordinary FULL/PREDICATE/LIMIT/etc. 
cases into separate files even though only `MULTI_COLUMN_DNF_SCAN` changes 
generated bytes (its c0 category corpus). This contradicts the benchmark 
contract that fixture keys contain only content-changing axes, multiplies the 
retained temp corpus, and gives identical data different footer/page-cache 
identities across operations. Add an explicit corpus/data-shape tag for the 
exceptional DNF contents while preserving the shared key for byte-identical 
operations.



##########
be/benchmark/parquet/benchmark_parquet_reader.hpp:
##########
@@ -408,6 +448,75 @@ inline VExprContextSPtr 
make_complex_residual_predicate(int selectivity_percent,
     return VExprContext::create_shared(std::move(compound));
 }
 
+inline VExprSPtr make_compound_predicate(TExprOpcode::type opcode, VExprSPtr 
left,
+                                         VExprSPtr right) {
+    const auto bool_type = make_nullable(std::make_shared<DataTypeUInt8>());
+    TExprNode node;
+    node.__set_node_type(TExprNodeType::COMPOUND_PRED);
+    node.__set_opcode(opcode);
+    node.__set_type(bool_type->to_thrift());
+    node.__set_num_children(2);
+    node.__set_is_nullable(true);
+    auto compound = VCompoundPred::create_shared(node);
+    compound->add_child(std::move(left));
+    compound->add_child(std::move(right));
+    return compound;
+}
+
+inline VExprContextSPtr make_multi_column_or_predicate(const ReaderScenario& 
scenario,
+                                                       const std::array<int, 
3>& positions,
+                                                       const DataTypePtr& 
value_type) {
+    const auto literal = [&](int value) -> VExprSPtr {
+        if (scenario.value_type == ValueType::DECIMAL64) {
+            return VLiteral::create_shared(
+                    remove_nullable(value_type),
+                    Field::create_field<TYPE_DECIMAL64>(Decimal64 {value * 
100}));
+        }
+        return VLiteral::create_shared(remove_nullable(value_type),
+                                       Field::create_field<TYPE_INT>(value));
+    };
+    const auto make_between = [&](int position) {
+        auto slot = [&] {
+            return VSlotRef::create_shared(position, position, -1, value_type,
+                                           "c" + std::to_string(position));
+        };
+        auto lower = make_int32_comparison("ge", TExprOpcode::GE, slot(), 
literal(0));
+        auto upper = make_int32_comparison("lt", TExprOpcode::LT, slot(),
+                                           
literal(scenario.selectivity_percent));
+        return make_compound_predicate(TExprOpcode::COMPOUND_AND, 
std::move(lower),
+                                       std::move(upper));
+    };
+    auto first_two = make_compound_predicate(TExprOpcode::COMPOUND_OR, 
make_between(positions[0]),

Review Comment:
   [P2] Make the simple-OR benchmark branch-sensitive
   
   All three predicate columns reuse the same DECIMAL `row % 100` values/nulls, 
and `make_multi_column_or_predicate()` applies the same interval to each one. 
Consequently `p(c0) OR p(c1) OR p(c2)` is indistinguishable from a single 
branch (or even an AND of the maps). Since `scan_reader()` checks only total 
row count, dropped branches and equal-cardinality wrong output can benchmark as 
correct. Give the columns/bounds deterministic cross-counterexamples and 
validate an independent row-id or payload checksum plus legacy/raw equality 
outside the timed region. This is separate from the existing DNF-fixture oracle 
thread.



-- 
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]

Reply via email to