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


##########
be/src/format_v2/parquet/reader/native/column_chunk_reader.cpp:
##########
@@ -1725,22 +1840,77 @@ Status ColumnChunkReader<IN_COLLECTION, 
OFFSET_INDEX>::filter_fixed_width_values
                 selection.total_values);
     }
 
+    VExprSPtrs raw_conjuncts;
+    VExprSPtrs null_map_conjuncts;
+    for (const auto& conjunct : conjuncts) {
+        if (conjunct->can_execute_on_null_map(_field_schema->data_type, 
column_id)) {
+            null_map_conjuncts.push_back(conjunct);
+        } else {
+            raw_conjuncts.push_back(conjunct);
+        }
+    }
     physical_matches->clear();
+    IColumn::Filter physical_conversion_nulls;
     if (selection.selected_values == 0) {
         RETURN_IF_ERROR(_page_decoder->skip_values(selection.total_values));
+    } else if (raw_conjuncts.empty()) {
+        RETURN_IF_ERROR(_page_decoder->skip_values(selection.total_values));

Review Comment:
   Predicate-only NULL evaluation cannot skip the payload here. An optional 
INT32 DATE containing INT32_MAX is converted to NULL by ordinary non-strict 
materialization (so IS NULL matches), while strict mode returns 
DataQualityError. This branch sees only a present definition level, treats the 
row as non-NULL, and suppresses the strict error. Please either feed SerDe 
conversion failures into `selected_nulls` or restrict the definition-only fast 
path to types whose non-NULL payload cannot fail conversion, with 
strict/non-strict differential tests.



##########
be/src/exprs/vcompound_pred.h:
##########
@@ -67,6 +67,52 @@ class VCompoundPred : public VectorizedFnCall {
         return Status::OK();
     }
 
+    bool can_execute_on_raw_fixed_values(const DataTypePtr& data_type,
+                                         int column_id) const override {
+        return !_children.empty() &&
+               (_op == TExprOpcode::COMPOUND_AND || _op == 
TExprOpcode::COMPOUND_OR ||
+                (_op == TExprOpcode::COMPOUND_NOT && _children.size() == 1)) &&

Review Comment:
   `COMPOUND_NOT` is not safe on this raw ABI because the decoder never 
evaluates its child for physical NULL rows. For example, `NOT(nullable_col <=> 
5)` must keep a NULL row (`NULL <=> 5` is false), but the reader never sends 
that row to the raw kernel and remaps it to false before NOT can apply. Keep 
NOT residual unless the reader can evaluate the expression's truth on physical 
NULLs, and add nullable fixed/binary coverage.



##########
be/src/core/data_type_serde/data_type_datetimev2_serde.cpp:
##########
@@ -710,6 +742,24 @@ Status 
DataTypeDateTimeV2SerDe::read_column_from_parquet(IColumn& column,
     return state.materialize_dictionary(column, source, num_values);
 }
 
+bool DataTypeDateTimeV2SerDe::supports_parquet_raw_predicate(
+        const ParquetDecodeContext& context) const {
+    return context.encoding != ParquetValueEncoding::DICTIONARY &&
+           (context.physical_type == ParquetPhysicalType::INT64 ||
+            context.physical_type == ParquetPhysicalType::INT96) &&
+           context.logical_type == ParquetLogicalType::TIMESTAMP;
+}
+
+Status DataTypeDateTimeV2SerDe::read_parquet_raw_predicate(
+        ParquetDecodeSource& source, const ParquetDecodeContext& context, 
size_t num_values,
+        bool enable_strict_mode, ParquetLogicalValueConsumer& consumer) const {
+    if (!supports_parquet_raw_predicate(context)) {
+        return Status::NotSupported("Unsupported Parquet raw predicate 
conversion for DATETIMEV2");
+    }
+    DateTimeV2PredicateParquetConsumer predicate_consumer(context, 
enable_strict_mode, consumer);

Review Comment:
   This consumer owns `_logical_values` and `_conversion_nulls`, but is created 
and destroyed for every read-range/page fragment. The same pattern exists in 
DATE/NUMBER/DECIMAL/TIME/TIMESTAMPTZ, and the reader also creates a second 
local conversion-null filter; a max-size Decimal256 fragment is about 2 MiB of 
POD scratch plus null buffers repeatedly allocated. The native-reader contract 
requires persistent bounded high-water scratch. Please move these buffers into 
persistent leaf-reader/caller-owned state and add reuse/retained-capacity 
coverage.



##########
be/src/format_v2/parquet/parquet_scan.cpp:
##########
@@ -1452,6 +1455,54 @@ bool get_typed_dictionary_raw_values(PrimitiveType 
primitive_type, const IColumn
     }
 }
 
+Status try_apply_runtime_filters_to_dictionary(size_t block_position,
+                                               const ParquetColumnSchema& 
column_schema,
+                                               const VExprContextSPtrs& 
conjuncts,
+                                               const IColumn& dictionary,
+                                               IColumn::Filter* 
dictionary_filter, bool* applied) {
+    DORIS_CHECK(dictionary_filter != nullptr);
+    DORIS_CHECK(applied != nullptr);
+    *applied = false;
+    if (!std::ranges::all_of(conjuncts, [](const auto& conjunct) {
+            return conjunct != nullptr && conjunct->root() != nullptr &&
+                   conjunct->root()->is_rf_wrapper() &&
+                   conjunct->root()->can_evaluate_dictionary_filter() &&
+                   conjunct->root()->get_impl() != nullptr;
+        })) {
+        return Status::OK();
+    }
+
+    Block dictionary_block;
+    const size_t dictionary_size = dictionary.size();
+    const auto dummy_type = std::make_shared<DataTypeUInt8>();
+    for (size_t position = 0; position < block_position; ++position) {
+        dictionary_block.insert(
+                {ColumnUInt8::create(dictionary_size, 0), dummy_type, 
"dictionary_dummy"});

Review Comment:
   Each placeholder here is a dense `dictionary_size` byte column, and 
`select_with_runtime_filter()` repeats the pattern with `selected_rows`. Since 
a predicate slot can follow a wide projection, auxiliary memory becomes 
O(block_position * rows): e.g. 1,000 prior slots × 100,000 dictionary entries 
is about 100 MB for one filter setup. Use shared constant/sparse placeholders 
or remap the single-column expression to position zero, and test a nonzero/wide 
slot.



##########
be/src/core/data_type_serde/data_type_datetimev2_serde.cpp:
##########
@@ -193,6 +197,34 @@ class RejectDateTimeV2BinaryConsumer final : public 
ParquetBinaryValueConsumer {
     }
 };
 
+class DateTimeV2PredicateParquetConsumer final : public 
ParquetFixedValueConsumer {
+public:
+    DateTimeV2PredicateParquetConsumer(const ParquetDecodeContext& context, 
bool enable_strict_mode,
+                                       ParquetLogicalValueConsumer& consumer)
+            : _context(context), _enable_strict_mode(enable_strict_mode), 
_consumer(consumer) {}
+
+    Status consume(const uint8_t* values, size_t num_values, size_t 
value_width) override {
+        _logical_values.clear();
+        _conversion_nulls.clear();
+        _conversion_nulls.resize_fill(num_values, 0);
+        ParquetMaterializationState state;
+        state.enable_strict_mode = _enable_strict_mode;
+        state.conversion_failure_null_map = &_conversion_nulls;

Review Comment:
   This local null map bypasses the established DATETIMEV2 compatibility 
policy. For nullable INT96 or UTC-adjusted TIMESTAMP overflow in non-strict 
mode, ordinary materialization sends the failure to compatibility scratch, 
stores the legacy zero DATETIME default, and leaves the output null bit clear. 
Here the same value becomes a synthetic NULL, so a raw predicate such as `col 
!= DATETIME '2020-01-01 00:00:00'` drops a row the ordinary path keeps. Route 
direct conversion through the same compatibility policy (or disable it for 
these timestamp modes) and add a pushdown-on/off differential test.



##########
be/src/format_v2/parquet/reader/native/column_chunk_reader.cpp:
##########
@@ -804,6 +825,55 @@ class FixedWidthPredicateConsumer final : public 
ParquetFixedValueConsumer {
     int _column_id;
     IColumn::Filter* _matches;
     IColumn* _projected_column;
+    IColumn::Filter* _conversion_nulls;
+};
+
+class BinaryPredicateConsumer final : public ParquetFixedValueConsumer,
+                                      public ParquetBinaryValueConsumer {
+public:
+    BinaryPredicateConsumer(const VExprSPtrs& conjuncts, DataTypePtr 
data_type, int column_id,
+                            IColumn::Filter* matches, IColumn* 
projected_column)
+            : _conjuncts(conjuncts),
+              _data_type(std::move(data_type)),
+              _column_id(column_id),
+              _matches(matches),
+              _projected_column(projected_column) {
+        DORIS_CHECK(_matches != nullptr);
+    }
+
+    Status consume(const uint8_t* values, size_t num_values, size_t 
value_width) override {
+        _refs.resize(num_values);

Review Comment:
   This reintroduces the string staging the native PLAIN contract is meant to 
avoid. `BinaryPredicateConsumer` does not override 
`consume_plain_byte_array()`, so the default allocates/fills a 
`vector<StringRef>` per fragment after the decoder has already produced 
payload/value offsets; FIXED_LEN uses this local `_refs` vector too, and 
projected survivors are inserted one row at a time below. Please consume the 
offset/span callback directly, keep reusable binary scratch in the leaf reader, 
and bulk-append projected survivor spans, with projected multi-page coverage.



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