This is an automated email from the ASF dual-hosted git repository.

Gabriel39 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new b759db26948 [fix](file) Revert split residual predicate ownership 
(#65998)
b759db26948 is described below

commit b759db26948cebb22206ffa3a5e0a969616f2616
Author: Gabriel <[email protected]>
AuthorDate: Fri Jul 24 18:08:11 2026 +0800

    [fix](file) Revert split residual predicate ownership (#65998)
---
 be/src/exec/operator/scan_operator.cpp             |  20 +-
 be/src/exec/operator/scan_operator.h               |   9 +-
 be/src/exec/scan/file_scanner_v2.cpp               | 145 ++-------
 be/src/exec/scan/file_scanner_v2.h                 |  33 +-
 be/src/exec/scan/scanner.cpp                       |  16 +-
 be/src/exec/scan/scanner.h                         |   8 -
 be/src/format_v2/column_mapper.cpp                 |  69 +----
 be/src/format_v2/column_mapper.h                   |  15 +-
 be/src/format_v2/file_reader.h                     |   4 +-
 be/src/format_v2/table/hudi_reader.cpp             |  22 --
 be/src/format_v2/table/hudi_reader.h               |   2 -
 .../iceberg_position_delete_sys_table_reader.cpp   |  31 +-
 be/src/format_v2/table/paimon_reader.cpp           |  22 --
 be/src/format_v2/table/paimon_reader.h             |   2 -
 be/src/format_v2/table_reader.cpp                  | 143 +--------
 be/src/format_v2/table_reader.h                    | 128 +++-----
 .../segment/adaptive_block_size_predictor.cpp      |   7 +-
 .../segment/adaptive_block_size_predictor.h        |   1 -
 be/test/exec/scan/file_scanner_v2_test.cpp         |  35 +--
 be/test/exec/scan/scanner_late_arrival_rf_test.cpp |  57 +---
 ...eberg_position_delete_sys_table_reader_test.cpp |  73 -----
 be/test/format_v2/column_mapper_test.cpp           |  68 +----
 be/test/format_v2/table/hudi_reader_test.cpp       | 147 ---------
 be/test/format_v2/table/iceberg_reader_test.cpp    |  11 +
 be/test/format_v2/table/paimon_reader_test.cpp     | 146 ---------
 be/test/format_v2/table_reader_test.cpp            | 340 +--------------------
 .../segment/adaptive_block_size_predictor_test.cpp |  12 -
 27 files changed, 142 insertions(+), 1424 deletions(-)

diff --git a/be/src/exec/operator/scan_operator.cpp 
b/be/src/exec/operator/scan_operator.cpp
index bc0c56dbfe7..3bb234d16b1 100644
--- a/be/src/exec/operator/scan_operator.cpp
+++ b/be/src/exec/operator/scan_operator.cpp
@@ -71,19 +71,13 @@ bool ScanLocalState<Derived>::should_run_serial() const {
     return _parent->cast<typename Derived::Parent>()._should_run_serial;
 }
 
-Status ScanLocalStateBase::update_late_arrival_runtime_filter(
-        RuntimeState* state, int applied_rf_num, int& arrived_rf_num,
-        VExprContextSPtrs& arrived_conjuncts) {
+Status ScanLocalStateBase::update_late_arrival_runtime_filter(RuntimeState* 
state,
+                                                              int& 
arrived_rf_num) {
     // Lock needed because _conjuncts can be accessed concurrently by multiple 
scanner threads
     LockGuard lock(_conjuncts_lock);
-    arrived_conjuncts.clear();
     size_t conjuncts_before = _conjuncts.size();
     RETURN_IF_ERROR(_helper.try_append_late_arrival_runtime_filter(state, 
_parent->row_descriptor(),
                                                                    
arrived_rf_num, _conjuncts));
-    if (_conjuncts.size() > conjuncts_before) {
-        VExprContextSPtrs appended(_conjuncts.begin() + conjuncts_before, 
_conjuncts.end());
-        _late_arrival_conjunct_batches.emplace_back(arrived_rf_num, 
std::move(appended));
-    }
     if (state->enable_adjust_conjunct_order_by_cost()) {
         std::ranges::stable_sort(_conjuncts, [](const auto& a, const auto& b) {
             return a->execute_cost() < b->execute_cost();
@@ -97,16 +91,6 @@ Status 
ScanLocalStateBase::update_late_arrival_runtime_filter(
     if (_conjuncts.size() > conjuncts_before) {
         RETURN_IF_ERROR(_on_runtime_filter_update());
     }
-    for (const auto& [batch_arrived_rf_num, batch] : 
_late_arrival_conjunct_batches) {
-        if (batch_arrived_rf_num <= applied_rf_num) {
-            continue;
-        }
-        for (const auto& conjunct : batch) {
-            VExprContextSPtr cloned;
-            RETURN_IF_ERROR(conjunct->clone(state, cloned));
-            arrived_conjuncts.push_back(std::move(cloned));
-        }
-    }
     return Status::OK();
 }
 
diff --git a/be/src/exec/operator/scan_operator.h 
b/be/src/exec/operator/scan_operator.h
index e955840f131..ae93e155852 100644
--- a/be/src/exec/operator/scan_operator.h
+++ b/be/src/exec/operator/scan_operator.h
@@ -21,8 +21,6 @@
 #include <optional>
 #include <set>
 #include <string>
-#include <utility>
-#include <vector>
 
 #include "common/status.h"
 #include "common/thread_safety_annotations.h"
@@ -98,9 +96,7 @@ public:
 
     uint64_t get_condition_cache_digest() const { return 
_condition_cache_digest; }
 
-    Status update_late_arrival_runtime_filter(RuntimeState* state, int 
applied_rf_num,
-                                              int& arrived_rf_num,
-                                              VExprContextSPtrs& 
arrived_conjuncts);
+    Status update_late_arrival_runtime_filter(RuntimeState* state, int& 
arrived_rf_num);
 
     Status clone_conjunct_ctxs(VExprContextSPtrs& scanner_conjuncts);
 
@@ -146,9 +142,6 @@ protected:
 
     AnnotatedMutex _conjuncts_lock;
     RuntimeFilterConsumerHelper _helper;
-    // Preserve append identity independently of the cost-sorted operator 
snapshot. Every scanner
-    // needs the exact RF contexts added since its own applied count.
-    std::vector<std::pair<int, VExprContextSPtrs>> 
_late_arrival_conjunct_batches;
     // magic number as seed to generate hash value for condition cache
     uint64_t _condition_cache_digest = 0;
     // condition cache filter stats
diff --git a/be/src/exec/scan/file_scanner_v2.cpp 
b/be/src/exec/scan/file_scanner_v2.cpp
index a20946cf095..cdaa3d2f3b8 100644
--- a/be/src/exec/scan/file_scanner_v2.cpp
+++ b/be/src/exec/scan/file_scanner_v2.cpp
@@ -24,7 +24,6 @@
 #include <map>
 #include <memory>
 #include <optional>
-#include <sstream>
 #include <string>
 #include <utility>
 
@@ -227,9 +226,7 @@ Status rewrite_slot_refs_to_global_index(
 #ifdef BE_TEST
 FileScannerV2::FileScannerV2(RuntimeState* state, RuntimeProfile* profile,
                              std::unique_ptr<format::TableReader> table_reader)
-        : Scanner(state, profile),
-          _table_reader(std::move(table_reader)),
-          _scanner_profile(profile) {}
+        : Scanner(state, profile), _table_reader(std::move(table_reader)) {}
 
 Status FileScannerV2::TEST_validate_scan_range(const TFileScanRangeParams& 
params,
                                                const TFileRangeDesc& range) {
@@ -329,9 +326,7 @@ FileScannerV2::FileScannerV2(RuntimeState* state, 
FileScanLocalState* local_stat
 
 Status FileScannerV2::init(RuntimeState* state, const VExprContextSPtrs& 
conjuncts) {
     RETURN_IF_ERROR(Scanner::init(state, conjuncts));
-    _initialize_scanner_residual_conjuncts();
     auto* profile = _local_state->scanner_profile();
-    _scanner_profile = profile;
     const auto hierarchy = file_scan_profile::ensure_hierarchy(profile);
     _scanner_total_timer = hierarchy.scanner;
     _io_timer = hierarchy.io;
@@ -365,11 +360,6 @@ Status FileScannerV2::init(RuntimeState* state, const 
VExprContextSPtrs& conjunc
             profile, "AdaptiveBatchActualBytes", TUnit::BYTES, 
file_scan_profile::SCANNER, 1);
     _adaptive_batch_probe_count_counter = ADD_CHILD_COUNTER_WITH_LEVEL(
             profile, "AdaptiveBatchProbeCount", TUnit::UNIT, 
file_scan_profile::SCANNER, 1);
-    _scanner_residual_filter_timer = ADD_CHILD_TIMER_WITH_LEVEL(
-            profile, "ScannerResidualFilterTime", file_scan_profile::SCANNER, 
1);
-    _scanner_residual_rows_filtered_counter = ADD_CHILD_COUNTER_WITH_LEVEL(
-            profile, "ScannerResidualRowsFiltered", TUnit::UNIT, 
file_scan_profile::SCANNER, 1);
-    _refresh_scanner_residual_profile();
     SCOPED_TIMER(_scanner_total_timer);
     SCOPED_TIMER(_init_timer);
     _file_cache_statistics = std::make_unique<io::FileCacheStatistics>();
@@ -411,7 +401,6 @@ Status FileScannerV2::_get_block_impl(RuntimeState* state, 
Block* block, bool* e
     SCOPED_TIMER(_get_block_timer);
     while (true) {
         RETURN_IF_CANCELLED(state);
-        RETURN_IF_ERROR(_sync_table_reader_conjuncts());
         if (!_has_prepared_split) {
             RETURN_IF_ERROR(_prepare_next_split(eof));
             if (*eof) {
@@ -461,33 +450,18 @@ Status FileScannerV2::_get_block_impl(RuntimeState* 
state, Block* block, bool* e
 }
 
 Status FileScannerV2::_filter_output_block(Block* block) {
-    if (_scanner_residual_conjuncts.empty() || block->rows() == 0) {
-        return Status::OK();
-    }
-    SCOPED_TIMER(_scanner_residual_filter_timer);
-    const size_t rows_before_filter = block->rows();
-    auto status = VExprContext::filter_block(_scanner_residual_conjuncts, 
block, block->columns());
-    if (!status.ok() && _params != nullptr &&
-        _get_current_format_type() == TFileFormatType::FORMAT_ORC) {
-        status.prepend("Orc row reader nextBatch failed. reason = ");
-    }
-    RETURN_IF_ERROR(status);
-    const int64_t filtered_rows = cast_set<int64_t>(rows_before_filter - 
block->rows());
-    _counter.num_rows_unselected += filtered_rows;
-    if (_scanner_residual_rows_filtered_counter != nullptr) {
-        COUNTER_UPDATE(_scanner_residual_rows_filtered_counter, filtered_rows);
-    }
-    return Status::OK();
-}
-
-size_t FileScannerV2::_last_block_rows_read(const Block& block) const {
-    const auto& stats = _table_reader->last_materialized_block_stats();
-    return stats.has_materialized_input ? stats.rows : block.rows();
+    return 
_contextualize_output_filter_status(Scanner::_filter_output_block(block),
+                                               _get_current_format_type());
 }
 
-size_t FileScannerV2::_last_block_bytes_read(const Block& block) const {
-    const auto& stats = _table_reader->last_materialized_block_stats();
-    return stats.has_materialized_input ? stats.allocated_bytes : 
block.allocated_bytes();
+Status FileScannerV2::_contextualize_output_filter_status(Status status,
+                                                          
TFileFormatType::type format_type) {
+    if (!status.ok() && format_type == TFileFormatType::FORMAT_ORC) {
+        // Error-preserving expressions cannot be reordered into the ORC 
reader and therefore run
+        // at the scanner boundary; keep their error context identical to ORC 
callback failures.
+        status.prepend("Orc row reader nextBatch failed. reason = ");
+    }
+    return status;
 }
 
 Status FileScannerV2::_prepare_next_split(bool* eos) {
@@ -573,7 +547,6 @@ Status FileScannerV2::_init_table_reader(const 
TFileRangeDesc& range) {
     RETURN_IF_ERROR(_table_reader->init({
             .projected_columns = _projected_columns,
             .conjuncts = std::move(table_conjuncts),
-            .table_reader_owned_conjunct_count = 
_table_reader_owned_conjunct_count,
             .format = file_format,
             .scan_params = const_cast<TFileScanRangeParams*>(_params),
             .io_ctx = _io_ctx,
@@ -584,9 +557,6 @@ Status FileScannerV2::_init_table_reader(const 
TFileRangeDesc& range) {
             .push_down_count_columns = std::move(push_down_count_columns),
             .condition_cache_digest = 
_local_state->get_condition_cache_digest(),
     }));
-    _table_reader_applied_rf_num = _applied_rf_num;
-    // RFs collected before TableReader initialization are already present in 
the full snapshot.
-    _late_arrival_rf_conjuncts.clear();
     return Status::OK();
 }
 
@@ -631,12 +601,15 @@ Status FileScannerV2::_prepare_table_reader_split(const 
TFileRangeDesc& range,
                                                   std::map<std::string, Field> 
partition_values) {
     format::FileFormat current_split_format;
     RETURN_IF_ERROR(_to_file_format(get_range_format_type(*_params, range), 
&current_split_format));
+    VExprContextSPtrs conjuncts;
+    RETURN_IF_ERROR(_build_table_conjuncts(&conjuncts));
     VExprContextSPtrs partition_prune_conjuncts;
     if (_state->query_options().enable_runtime_filter_partition_prune) {
         RETURN_IF_ERROR(_build_table_conjuncts(&partition_prune_conjuncts));
     }
     RETURN_IF_ERROR(_table_reader->prepare_split({
             .partition_values = std::move(partition_values),
+            .conjuncts = std::move(conjuncts),
             .partition_prune_conjuncts = std::move(partition_prune_conjuncts),
             // A metadata COUNT split may span scheduler turns. Do not enter 
that irreversible
             // synthetic-row path while a runtime filter can still arrive 
between batches.
@@ -826,15 +799,10 @@ format::ColumnDefinition 
FileScannerV2::_build_table_column(const SlotDescriptor
 }
 
 Status FileScannerV2::_build_table_conjuncts(VExprContextSPtrs* conjuncts) 
const {
-    return _build_table_conjuncts(_conjuncts, conjuncts);
-}
-
-Status FileScannerV2::_build_table_conjuncts(const VExprContextSPtrs& source,
-                                             VExprContextSPtrs* conjuncts) 
const {
     DORIS_CHECK(conjuncts != nullptr);
     conjuncts->clear();
-    conjuncts->reserve(source.size());
-    for (const auto& conjunct : source) {
+    conjuncts->reserve(_conjuncts.size());
+    for (const auto& conjunct : _conjuncts) {
         VExprSPtr root;
         RETURN_IF_ERROR(format::clone_table_expr_tree(conjunct->root(), 
&root));
         RETURN_IF_ERROR(rewrite_slot_refs_to_global_index(&root, 
_slot_id_to_global_index));
@@ -843,68 +811,6 @@ Status FileScannerV2::_build_table_conjuncts(const 
VExprContextSPtrs& source,
     return Status::OK();
 }
 
-size_t FileScannerV2::_safe_conjunct_prefix_size(const VExprContextSPtrs& 
conjuncts) {
-    for (size_t conjunct_index = 0; conjunct_index < conjuncts.size(); 
++conjunct_index) {
-        if 
(!format::TableReader::is_safe_to_pre_execute(conjuncts[conjunct_index])) {
-            return conjunct_index;
-        }
-    }
-    return conjuncts.size();
-}
-
-void FileScannerV2::_initialize_scanner_residual_conjuncts() {
-    _table_reader_owned_conjunct_count = 
_safe_conjunct_prefix_size(_conjuncts);
-    // Preserve the entire suffix, not only the unsafe expression. Otherwise a 
later safe
-    // predicate could run below Scanner before a stateful/error-preserving 
ordering barrier.
-    _scanner_residual_conjuncts.assign(
-            _conjuncts.begin() + 
cast_set<ptrdiff_t>(_table_reader_owned_conjunct_count),
-            _conjuncts.end());
-    _refresh_scanner_residual_profile();
-}
-
-void FileScannerV2::_refresh_scanner_residual_profile() {
-    if (_scanner_profile == nullptr || _scanner_residual_conjuncts.empty()) {
-        return;
-    }
-    std::ostringstream predicates;
-    predicates << "[";
-    for (size_t conjunct_index = 0; conjunct_index < 
_scanner_residual_conjuncts.size();
-         ++conjunct_index) {
-        if (conjunct_index > 0) {
-            predicates << ", ";
-        }
-        predicates << 
_scanner_residual_conjuncts[conjunct_index]->root()->debug_string();
-    }
-    predicates << "]";
-    _scanner_profile->add_info_string("ScannerResidualPredicates", 
predicates.str());
-}
-
-Status FileScannerV2::_sync_table_reader_conjuncts() {
-    if (_table_reader == nullptr) {
-        return Status::OK();
-    }
-    if (_table_reader_applied_rf_num == _applied_rf_num) {
-        return Status::OK();
-    }
-    VExprContextSPtrs appended;
-    RETURN_IF_ERROR(_build_table_conjuncts(_late_arrival_rf_conjuncts, 
&appended));
-    const size_t owned_count = _scanner_residual_conjuncts.empty()
-                                       ? 
_safe_conjunct_prefix_size(_late_arrival_rf_conjuncts)
-                                       : 0;
-    // Preserve existing expression state and append the identity-tracked RF 
delta. Cost sorting
-    // may move a late RF ahead of an old stateful predicate in the full 
scanner snapshot.
-    RETURN_IF_ERROR(_table_reader->append_conjuncts_with_ownership(appended, 
owned_count));
-    _table_reader_owned_conjunct_count += owned_count;
-    _scanner_residual_conjuncts.insert(
-            _scanner_residual_conjuncts.end(),
-            _late_arrival_rf_conjuncts.begin() + 
cast_set<ptrdiff_t>(owned_count),
-            _late_arrival_rf_conjuncts.end());
-    _refresh_scanner_residual_profile();
-    _late_arrival_rf_conjuncts.clear();
-    _table_reader_applied_rf_num = _applied_rf_num;
-    return Status::OK();
-}
-
 TFileFormatType::type FileScannerV2::_get_current_format_type() const {
     return get_range_format_type(*_params, _current_range);
 }
@@ -1028,19 +934,17 @@ void FileScannerV2::_update_adaptive_batch_size(const 
Block& block) {
     if (!_should_run_adaptive_batch_size()) {
         return;
     }
-    const auto& stats = _table_reader->last_materialized_block_stats();
-    const size_t rows = stats.has_materialized_input ? stats.rows : 
block.rows();
-    const size_t bytes = stats.has_materialized_input ? stats.bytes : 
block.bytes();
-    COUNTER_SET(_adaptive_batch_actual_bytes_counter, 
static_cast<int64_t>(bytes));
-    if (rows == 0) {
+    COUNTER_SET(_adaptive_batch_actual_bytes_counter, 
static_cast<int64_t>(block.bytes()));
+    if (block.rows() == 0) {
         return;
     }
-    // Residual predicates run after wide table columns are materialized. 
Learn from that pre-filter
-    // shape so selective predicates cannot make the next reader batch 
dangerously large.
+    // The sample is taken after TableReader has finalized file-local columns 
to table columns.
+    // This matches the memory shape seen by upstream operators and catches 
very wide nested
+    // columns, such as map/string payloads, after the first probe batch.
     if (!_block_size_predictor->has_history()) {
         COUNTER_UPDATE(_adaptive_batch_probe_count_counter, 1);
     }
-    _block_size_predictor->update(rows, bytes);
+    _block_size_predictor->update(block);
 }
 
 Status FileScannerV2::close(RuntimeState* state) {
@@ -1241,8 +1145,9 @@ void 
FileScannerV2::_report_file_reader_predicate_filtered_rows() {
     const int64_t filtered_rows = _io_ctx != nullptr ? 
_io_ctx->predicate_filtered_rows : 0;
     const int64_t filtered_delta = filtered_rows - 
_reported_predicate_filtered_rows;
     if (filtered_delta > 0) {
-        // FileReader and TableReader both report their owned predicate rows 
through the shared IO
-        // context. Preserve scanner-level load statistics without 
re-evaluating either predicate.
+        // File readers can evaluate localized conjuncts before a block 
reaches Scanner. Count
+        // those rows as scanner-level unselected rows so load statistics stay 
identical no matter
+        // whether a predicate is pushed down or evaluated by 
Scanner::_filter_output_block().
         _counter.num_rows_unselected += filtered_delta;
         _reported_predicate_filtered_rows = filtered_rows;
     }
diff --git a/be/src/exec/scan/file_scanner_v2.h 
b/be/src/exec/scan/file_scanner_v2.h
index c68cfb1db8b..fc217506eea 100644
--- a/be/src/exec/scan/file_scanner_v2.h
+++ b/be/src/exec/scan/file_scanner_v2.h
@@ -88,22 +88,15 @@ public:
             RuntimeProfile* profile, const io::FileCacheStatistics& 
file_cache_statistics);
     static bool TEST_should_skip_not_found(const Status& status, bool 
ignore_not_found);
     static bool TEST_should_skip_empty(const Status& status, bool stopped);
+    static Status TEST_contextualize_output_filter_status(Status status,
+                                                          
TFileFormatType::type format_type) {
+        return _contextualize_output_filter_status(std::move(status), 
format_type);
+    }
     static bool TEST_should_run_adaptive_batch_size(bool predictor_initialized,
                                                     bool 
current_split_uses_metadata_count) {
         return _should_run_adaptive_batch_size(predictor_initialized,
                                                
current_split_uses_metadata_count);
     }
-    void TEST_set_scanner_conjuncts(VExprContextSPtrs conjuncts) {
-        _conjuncts = std::move(conjuncts);
-        _initialize_scanner_residual_conjuncts();
-    }
-    Status TEST_filter_output_block(Block* block) { return 
_filter_output_block(block); }
-    size_t TEST_table_reader_owned_conjunct_count() const {
-        return _table_reader_owned_conjunct_count;
-    }
-    size_t TEST_scanner_residual_conjunct_count() const {
-        return _scanner_residual_conjuncts.size();
-    }
 #endif
 
     FileScannerV2(RuntimeState* state, FileScanLocalState* parent, int64_t 
limit,
@@ -122,8 +115,6 @@ public:
 protected:
     Status _get_block_impl(RuntimeState* state, Block* block, bool* eof) 
override;
     Status _filter_output_block(Block* block) override;
-    size_t _last_block_rows_read(const Block& block) const override;
-    size_t _last_block_bytes_read(const Block& block) const override;
     void _collect_profile_before_close() override;
     bool _should_update_load_counters() const override;
 
@@ -142,6 +133,8 @@ private:
                                        std::map<std::string, Field> 
partition_values);
     static bool _should_skip_not_found(const Status& status, bool 
ignore_not_found);
     static bool _should_skip_empty(const Status& status, bool stopped);
+    static Status _contextualize_output_filter_status(Status status,
+                                                      TFileFormatType::type 
format_type);
     bool _should_enable_file_meta_cache() const;
     std::optional<format::GlobalRowIdContext> _create_global_rowid_context(
             const TFileRangeDesc& range) const;
@@ -153,12 +146,6 @@ private:
     Status _build_default_expr(const TFileScanSlotInfo& slot_info, 
VExprContextSPtr* ctx) const;
     static format::ColumnDefinition _build_table_column(const SlotDescriptor* 
slot_desc);
     Status _build_table_conjuncts(VExprContextSPtrs* conjuncts) const;
-    Status _build_table_conjuncts(const VExprContextSPtrs& source,
-                                  VExprContextSPtrs* conjuncts) const;
-    Status _sync_table_reader_conjuncts();
-    static size_t _safe_conjunct_prefix_size(const VExprContextSPtrs& 
conjuncts);
-    void _initialize_scanner_residual_conjuncts();
-    void _refresh_scanner_residual_profile();
     static Status _to_file_format(TFileFormatType::type format_type,
                                   format::FileFormat* file_format);
     void _reset_adaptive_batch_size_state();
@@ -194,10 +181,6 @@ private:
     std::string _current_range_path;
 
     std::unique_ptr<format::TableReader> _table_reader;
-    size_t _table_reader_owned_conjunct_count = 0;
-    // Scanner owns one persistent context vector for the first unsafe 
conjunct and every later
-    // conjunct. Hybrid child readers may be recreated or switched, but this 
state must not be.
-    VExprContextSPtrs _scanner_residual_conjuncts;
     std::vector<format::ColumnDefinition> _projected_columns;
     // File formats without embedded schema, such as CSV, still need the FE 
slot descriptors in
     // file-column order. This mirrors old FileScanner::_file_slot_descs and 
is passed only to
@@ -231,9 +214,6 @@ private:
     RuntimeProfile::Counter* _adaptive_batch_predicted_rows_counter = nullptr;
     RuntimeProfile::Counter* _adaptive_batch_actual_bytes_counter = nullptr;
     RuntimeProfile::Counter* _adaptive_batch_probe_count_counter = nullptr;
-    RuntimeProfile::Counter* _scanner_residual_filter_timer = nullptr;
-    RuntimeProfile::Counter* _scanner_residual_rows_filtered_counter = nullptr;
-    RuntimeProfile* _scanner_profile = nullptr;
     std::unique_ptr<AdaptiveBlockSizePredictor> _block_size_predictor;
     int64_t _reported_predicate_filtered_rows = 0;
     int64_t _reported_condition_cache_hit_count = 0;
@@ -243,7 +223,6 @@ private:
     int64_t _last_bytes_read_from_local = 0;
     int64_t _last_bytes_read_from_remote = 0;
     int64_t _reported_io_read_time = 0;
-    int _table_reader_applied_rf_num = 0;
 };
 
 } // namespace doris
diff --git a/be/src/exec/scan/scanner.cpp b/be/src/exec/scan/scanner.cpp
index 138b3910d4f..3eaf5ee77eb 100644
--- a/be/src/exec/scan/scanner.cpp
+++ b/be/src/exec/scan/scanner.cpp
@@ -19,8 +19,6 @@
 
 #include <glog/logging.h>
 
-#include <iterator>
-
 #include "common/config.h"
 #include "common/status.h"
 #include "core/block/column_with_type_and_name.h"
@@ -162,11 +160,8 @@ Status Scanner::get_block(RuntimeState* state, Block* 
block, bool* eof) {
                     DCHECK(block->rows() == 0);
                     break;
                 }
-                // Some scanners apply owned predicates before returning the 
block. Account the
-                // materialized input, not only survivors, so the per-turn 
progress bound remains
-                // effective for highly selective predicates.
-                _num_rows_read += _last_block_rows_read(*block);
-                _num_byte_read += _last_block_bytes_read(*block);
+                _num_rows_read += block->rows();
+                _num_byte_read += block->allocated_bytes();
             }
 
             // 2. Filter the output block finally.
@@ -261,9 +256,7 @@ Status Scanner::try_append_late_arrival_runtime_filter() {
     }
     DCHECK(_applied_rf_num < _total_rf_num);
     int arrived_rf_num = 0;
-    VExprContextSPtrs arrived_conjuncts;
-    RETURN_IF_ERROR(_local_state->update_late_arrival_runtime_filter(
-            _state, _applied_rf_num, arrived_rf_num, arrived_conjuncts));
+    RETURN_IF_ERROR(_local_state->update_late_arrival_runtime_filter(_state, 
arrived_rf_num));
 
     if (arrived_rf_num == _applied_rf_num) {
         // No newly arrived runtime filters, just return;
@@ -273,9 +266,6 @@ Status Scanner::try_append_late_arrival_runtime_filter() {
     // avoid conjunct destroy in used by storage layer
     _conjuncts.clear();
     RETURN_IF_ERROR(_local_state->clone_conjunct_ctxs(_conjuncts));
-    _late_arrival_rf_conjuncts.insert(_late_arrival_rf_conjuncts.end(),
-                                      
std::make_move_iterator(arrived_conjuncts.begin()),
-                                      
std::make_move_iterator(arrived_conjuncts.end()));
     _applied_rf_num = arrived_rf_num;
     return Status::OK();
 }
diff --git a/be/src/exec/scan/scanner.h b/be/src/exec/scan/scanner.h
index 8f9753cf112..1bf4cdb5bb2 100644
--- a/be/src/exec/scan/scanner.h
+++ b/be/src/exec/scan/scanner.h
@@ -231,11 +231,6 @@ public:
     void update_block_avg_bytes(size_t block_avg_bytes) { _block_avg_bytes = 
block_avg_bytes; }
 
 protected:
-    virtual size_t _last_block_rows_read(const Block& block) const { return 
block.rows(); }
-    virtual size_t _last_block_bytes_read(const Block& block) const {
-        return block.allocated_bytes();
-    }
-
     RuntimeState* _state = nullptr;
     ScanLocalStateBase* _local_state = nullptr;
 
@@ -263,9 +258,6 @@ protected:
     // Cloned from _conjuncts of scan node.
     // It includes predicate in SQL and runtime filters.
     VExprContextSPtrs _conjuncts;
-    // Exact append-only RF delta for readers that preserve state across 
multiple splits. It must
-    // not be reconstructed by position from the cost-sorted full conjunct 
snapshot.
-    VExprContextSPtrs _late_arrival_rf_conjuncts;
     VExprContextSPtrs _projections;
     // Used in common subexpression elimination to compute intermediate 
results.
     std::vector<VExprContextSPtrs> _intermediate_projections;
diff --git a/be/src/format_v2/column_mapper.cpp 
b/be/src/format_v2/column_mapper.cpp
index ed0dcda9739..c3dbe3fa9e7 100644
--- a/be/src/format_v2/column_mapper.cpp
+++ b/be/src/format_v2/column_mapper.cpp
@@ -399,33 +399,6 @@ std::string TableColumnMapperOptions::debug_string() const 
{
     return out.str();
 }
 
-bool requires_char_or_varchar_truncation(const ColumnMapping& mapping) {
-    if (mapping.table_type == nullptr) {
-        return false;
-    }
-    const auto table_type = remove_nullable(mapping.table_type);
-    const auto primitive_type = table_type->get_primitive_type();
-    if (primitive_type != TYPE_VARCHAR && primitive_type != TYPE_CHAR) {
-        return false;
-    }
-    const auto target_len = assert_cast<const 
DataTypeString*>(table_type.get())->len();
-    if (target_len <= 0) {
-        return false;
-    }
-    if (mapping.file_type == nullptr) {
-        return true;
-    }
-    const auto file_type = remove_nullable(mapping.file_type);
-    DORIS_CHECK(file_type != nullptr);
-    int file_len = -1;
-    if (file_type->get_primitive_type() == TYPE_VARCHAR ||
-        file_type->get_primitive_type() == TYPE_CHAR ||
-        file_type->get_primitive_type() == TYPE_STRING) {
-        file_len = assert_cast<const DataTypeString*>(file_type.get())->len();
-    }
-    return file_len < 0 || target_len < file_len;
-}
-
 std::string ColumnDefinition::debug_string() const {
     std::ostringstream out;
     out << "ColumnDefinition{name=" << name << ", identifier=" << 
field_debug_string(identifier)
@@ -2188,7 +2161,7 @@ Status TableColumnMapper::_build_filter_entries(const 
FileScanRequest& file_requ
 Status TableColumnMapper::create_scan_request(
         const std::vector<TableFilter>& table_filters,
         const std::vector<ColumnDefinition>& projected_columns, 
FileScanRequest* file_request,
-        RuntimeState* runtime_state, FilterLocalizationResult* 
localization_result) {
+        RuntimeState* runtime_state) {
     // FileReader evaluates expressions against a file-local block. This 
mapper owns the
     // table-column to file-column conversion, so it also owns the file-local 
block positions.
     file_request->predicate_columns.clear();
@@ -2224,8 +2197,7 @@ Status TableColumnMapper::create_scan_request(
     // 2. Build referenced predicate columns
     // Hidden filter mappings must be built before localizing filters, so that 
they can be localized together with visible mappings and referenced by 
localized filter expressions.
     RETURN_IF_ERROR(_build_hidden_filter_mappings(table_filters));
-    RETURN_IF_ERROR(
-            localize_filters(table_filters, file_request, runtime_state, 
localization_result));
+    RETURN_IF_ERROR(localize_filters(table_filters, file_request, 
runtime_state));
     for (const auto& mapping : _hidden_mappings) {
         if (!mapping.file_local_id.has_value()) {
             continue;
@@ -2239,9 +2211,9 @@ Status TableColumnMapper::create_scan_request(
         if (is_visible_output) {
             continue;
         }
-        // A localized predicate is enforced exactly before TableReader 
materializes output. Only
-        // truly hidden mappings are absent from the final table block and may 
discard their
-        // payload after that file-local evaluation.
+        // File-local filtering is an optimization; Scanner still evaluates 
the original
+        // table-level conjunct after TableReader returns. Only truly hidden 
mappings are absent
+        // from that scanner-visible block and may safely discard their 
payload here.
         if (std::ranges::any_of(file_request->predicate_columns,
                                 [local_id](const LocalColumnIndex& projection) 
{
                                     return projection.column_id() == local_id;
@@ -2290,11 +2262,7 @@ ColumnMapping* 
TableColumnMapper::_find_filter_mapping(GlobalIndex global_index)
 
 Status TableColumnMapper::localize_filters(const std::vector<TableFilter>& 
table_filters,
                                            FileScanRequest* file_request,
-                                           RuntimeState* runtime_state,
-                                           FilterLocalizationResult* 
localization_result) {
-    if (localization_result != nullptr) {
-        localization_result->localized_filters.assign(table_filters.size(), 
false);
-    }
+                                           RuntimeState* runtime_state) {
     std::set<LocalColumnId> localized_predicate_columns;
     FilterProjectionMap filter_projections;
     auto filter_mappings = _filter_visible_mappings();
@@ -2332,28 +2300,17 @@ Status TableColumnMapper::localize_filters(const 
std::vector<TableFilter>& table
     // This keeps expression localization independent from filter iteration 
order.
     filter_mappings = _filter_visible_mappings();
     const auto global_to_file_slot = 
build_file_slot_rewrite_map(filter_mappings, _filter_entries);
-    for (size_t filter_index = 0; filter_index < table_filters.size(); 
++filter_index) {
-        const auto& table_filter = table_filters[filter_index];
+    for (const auto& table_filter : table_filters) {
         if (table_filter.conjunct != nullptr && table_filter.conjunct->root() 
!= nullptr) {
             const auto root = table_filter.conjunct->root();
             const auto impl = root->get_impl();
             const auto predicate = impl != nullptr ? impl : root;
-            if (!table_filter.can_localize || !predicate->is_deterministic() ||
+            if (!predicate->is_deterministic() ||
                 !table_filter_has_only_local_entries(table_filter, 
_filter_entries)) {
                 continue;
             }
-            if (runtime_state != nullptr &&
-                
runtime_state->query_options().truncate_char_or_varchar_columns &&
-                std::ranges::any_of(table_filter.global_indices, 
[&](GlobalIndex global_index) {
-                    const auto* mapping = _find_filter_mapping(global_index);
-                    return mapping != nullptr && 
requires_char_or_varchar_truncation(*mapping);
-                })) {
-                // The table predicate observes the bounded value after 
finalize; evaluating it on
-                // a wider file string would change equality and range 
semantics.
-                continue;
-            }
-            // FileReader becomes the exact owner only for a stable predicate 
whose complete
-            // expression can be rewritten against this split's physical 
schema.
+            // Scanner evaluates the original conjunct after final 
materialization. Only predicates
+            // whose result is stable across repeated execution may also run 
as a file-local copy.
             RewriteContext rewrite_context {.runtime_state = runtime_state};
             VExprSPtr rewrite_root;
             Status clone_status;
@@ -2364,7 +2321,8 @@ Status TableColumnMapper::localize_filters(const 
std::vector<TableFilter>& table
                 // `element_at(MAP_VALUES(m)[1], 'age') > 30`. The current 
file-local rewrite only
                 // understands top-level slots and struct-element paths rooted 
at top-level slots;
                 // cloning such expressions can hit the generic TExpr 
complex-type limitation.
-                // Leave them for TableReader after final table-schema 
materialization.
+                // Leave them above TableReader, where Scanner evaluates the 
original table-level
+                // conjunct after final materialization.
 #ifndef NDEBUG
                 return Status::InternalError(
                         "Failed to clone table filter for file-local rewrite: 
{}, expr={}",
@@ -2400,9 +2358,6 @@ Status TableColumnMapper::localize_filters(const 
std::vector<TableFilter>& table
             auto localized_conjunct = 
VExprContext::create_shared(std::move(localized_root));
             
RETURN_IF_ERROR(rewrite_context.prepare_created_exprs(localized_conjunct.get()));
             file_request->conjuncts.push_back(std::move(localized_conjunct));
-            if (localization_result != nullptr) {
-                localization_result->localized_filters[filter_index] = true;
-            }
             for (const auto global_index : table_filter.global_indices) {
                 const auto* mapping = _find_filter_mapping(global_index);
                 if (mapping != nullptr && mapping->file_local_id.has_value() &&
diff --git a/be/src/format_v2/column_mapper.h b/be/src/format_v2/column_mapper.h
index 0860b687e4a..e03f836061e 100644
--- a/be/src/format_v2/column_mapper.h
+++ b/be/src/format_v2/column_mapper.h
@@ -40,13 +40,6 @@ namespace doris::format {
 struct ColumnDefinition;
 struct TableFilter;
 
-// Reports which table filters were fully rewritten into exact file-local 
predicates for the
-// current split. The result is aligned with the TableFilter input vector and 
must not be reused for
-// another split because schema evolution can change localization 
independently for every file.
-struct FilterLocalizationResult {
-    std::vector<bool> localized_filters;
-};
-
 enum class TableColumnMappingMode {
     // Match by ColumnDefinition::identifier TYPE_INT as field id.
     BY_FIELD_ID,
@@ -172,8 +165,6 @@ struct TableColumnMapperOptions {
     std::string debug_string() const;
 };
 
-bool requires_char_or_varchar_truncation(const ColumnMapping& mapping);
-
 Status clone_table_expr_tree(const VExprSPtr& expr, VExprSPtr* cloned_expr);
 const Field* find_partition_value(const ColumnDefinition& table_column,
                                   const std::map<std::string, Field>& 
partition_values);
@@ -205,8 +196,7 @@ public:
     virtual Status create_scan_request(const std::vector<TableFilter>& 
table_filters,
                                        const std::vector<ColumnDefinition>& 
projected_columns,
                                        FileScanRequest* file_request,
-                                       RuntimeState* runtime_state = nullptr,
-                                       FilterLocalizationResult* 
localization_result = nullptr);
+                                       RuntimeState* runtime_state = nullptr);
 
     // Localize table-level filters to the file schema.
     // Trivial mappings can copy structured predicates directly. Type changes 
may be localized with
@@ -214,8 +204,7 @@ public:
     // table-level finalize/filter fallback.
     virtual Status localize_filters(const std::vector<TableFilter>& 
table_filters,
                                     FileScanRequest* file_request,
-                                    RuntimeState* runtime_state = nullptr,
-                                    FilterLocalizationResult* 
localization_result = nullptr);
+                                    RuntimeState* runtime_state = nullptr);
     void clear() {
         _mappings.clear();
         _hidden_mappings.clear();
diff --git a/be/src/format_v2/file_reader.h b/be/src/format_v2/file_reader.h
index 1f90957064b..5f959c3e672 100644
--- a/be/src/format_v2/file_reader.h
+++ b/be/src/format_v2/file_reader.h
@@ -76,9 +76,7 @@ struct FileScanRequest {
     std::vector<LocalColumnId> predicate_only_columns;
     // file-local column id -> file-local output block position.
     std::map<LocalColumnId, LocalIndex> local_positions;
-    // Row-level filters converted to file-local expressions from table-level 
predicates. Readers
-    // must enforce these exactly on returned rows; metadata pruning alone 
does not transfer
-    // predicate ownership away from TableReader.
+    // Row-level filters converted to file-local expressions from table-level 
predicates.
     VExprContextSPtrs conjuncts;
     // Delete predicates converted to file-local expressions. A TRUE result 
means that the row is
     // deleted, so readers must invert each result when building their keep 
filter.
diff --git a/be/src/format_v2/table/hudi_reader.cpp 
b/be/src/format_v2/table/hudi_reader.cpp
index 271f14a7b48..ee24d0f9ad7 100644
--- a/be/src/format_v2/table/hudi_reader.cpp
+++ b/be/src/format_v2/table/hudi_reader.cpp
@@ -125,27 +125,6 @@ void HudiHybridReader::set_batch_size(size_t batch_size) {
     }
 }
 
-Status HudiHybridReader::append_conjuncts(const VExprContextSPtrs& conjuncts) {
-    // The wrapper snapshot initializes future children, while every existing 
child needs the same
-    // late RF immediately so active and later reused splits keep identical 
predicate ownership.
-    const size_t owned_count =
-            
_appended_table_reader_owned_conjunct_count.value_or(conjuncts.size());
-    RETURN_IF_ERROR(format::TableReader::append_conjuncts(conjuncts));
-    if (_native_reader != nullptr) {
-        
RETURN_IF_ERROR(_native_reader->append_conjuncts_with_ownership(conjuncts, 
owned_count));
-    }
-    if (_jni_reader != nullptr) {
-        
RETURN_IF_ERROR(_jni_reader->append_conjuncts_with_ownership(conjuncts, 
owned_count));
-    }
-    return Status::OK();
-}
-
-const format::MaterializedBlockStats& 
HudiHybridReader::last_materialized_block_stats() const {
-    // FileScannerV2 budgets cooperative work from the child that actually 
materialized the block.
-    return _current_split_reader != nullptr ? 
_current_split_reader->last_materialized_block_stats()
-                                            : 
format::TableReader::last_materialized_block_stats();
-}
-
 int64_t HudiHybridReader::condition_cache_hit_count() const {
     // Keep the wrapper count cumulative across native/JNI dispatch so 
scanner-level delta
     // accounting neither loses a child hit nor observes a counter reset on a 
split switch.
@@ -197,7 +176,6 @@ Status 
HudiHybridReader::_init_child_reader(format::TableReader* reader,
     RETURN_IF_ERROR(reader->init({
             .projected_columns = _projected_columns,
             .conjuncts = std::move(conjuncts),
-            .table_reader_owned_conjunct_count = 
_table_reader_owned_conjunct_count,
             .format = file_format,
             .scan_params = _scan_params,
             .io_ctx = _io_ctx,
diff --git a/be/src/format_v2/table/hudi_reader.h 
b/be/src/format_v2/table/hudi_reader.h
index c13ac1215d7..dbb6f5e8231 100644
--- a/be/src/format_v2/table/hudi_reader.h
+++ b/be/src/format_v2/table/hudi_reader.h
@@ -65,8 +65,6 @@ public:
     Status abort_split() override;
     Status close() override;
     void set_batch_size(size_t batch_size) override;
-    Status append_conjuncts(const VExprContextSPtrs& conjuncts) override;
-    const format::MaterializedBlockStats& last_materialized_block_stats() 
const override;
     int64_t condition_cache_hit_count() const override;
 
 #ifdef BE_TEST
diff --git 
a/be/src/format_v2/table/iceberg_position_delete_sys_table_reader.cpp 
b/be/src/format_v2/table/iceberg_position_delete_sys_table_reader.cpp
index 6ab67caa77d..76d39c72fb1 100644
--- a/be/src/format_v2/table/iceberg_position_delete_sys_table_reader.cpp
+++ b/be/src/format_v2/table/iceberg_position_delete_sys_table_reader.cpp
@@ -163,12 +163,6 @@ Status 
IcebergPositionDeleteSysTableV2Reader::prepare_split(
         const format::SplitReadOptions& options) {
     RETURN_IF_ERROR(close());
     RETURN_IF_ERROR(format::TableReader::prepare_split(options));
-    if (current_split_pruned()) {
-        return Status::OK();
-    }
-    // This synthetic reader has no physical schema where a predicate can be 
localized, so every
-    // split predicate must run after its system-table columns have been 
materialized.
-    RETURN_IF_ERROR(_prepare_all_conjuncts_as_remaining());
     // The inner delete-file reader has distinct counters, so the outer 
preparation can safely
     // contain its cache miss/open work without re-entering the same 
RuntimeProfile timer.
     SCOPED_TIMER(_profile.total_timer);
@@ -181,7 +175,6 @@ Status IcebergPositionDeleteSysTableV2Reader::prepare_split(
 Status IcebergPositionDeleteSysTableV2Reader::get_block(Block* block, bool* 
eos) {
     SCOPED_TIMER(_profile.total_timer);
     SCOPED_TIMER(_profile.exec_timer);
-    _reset_materialized_block_stats();
     DORIS_CHECK(block != nullptr);
     DORIS_CHECK(eos != nullptr);
     DORIS_CHECK(block->columns() == _projected_columns.size());
@@ -199,19 +192,9 @@ Status 
IcebergPositionDeleteSysTableV2Reader::get_block(Block* block, bool* eos)
         return Status::OK();
     }
 
+    size_t read_rows = 0;
     if (_delete_file_kind == DeleteFileKind::DELETION_VECTOR) {
-        size_t read_rows = 0;
-        RETURN_IF_ERROR(_append_deletion_vector_block(block, &read_rows, eos));
-        if (read_rows > 0) {
-            _record_materialized_block_stats(*block, read_rows);
-            RETURN_IF_ERROR(_filter_remaining_conjuncts(block, &read_rows));
-        }
-        if (read_rows == 0) {
-            // Yield after one deletion-vector batch so cancellation and 
Scanner row budgets are
-            // observed even when residual predicates reject every synthesized 
row.
-            block->clear_column_data(_projected_columns.size());
-        }
-        return Status::OK();
+        return _append_deletion_vector_block(block, &read_rows, eos);
     }
 
     DORIS_CHECK(_position_reader != nullptr);
@@ -225,18 +208,8 @@ Status 
IcebergPositionDeleteSysTableV2Reader::get_block(Block* block, bool* eos)
         RETURN_IF_ERROR(_position_reader->get_block(&delete_block, 
&position_reader_eof));
         const size_t delete_rows = delete_block.rows();
         if (delete_rows > 0) {
-            size_t read_rows = 0;
             RETURN_IF_ERROR(
                     _append_position_delete_block(block, delete_block, 
delete_rows, &read_rows));
-            _record_materialized_block_stats(*block, read_rows);
-            RETURN_IF_ERROR(_filter_remaining_conjuncts(block, &read_rows));
-            if (read_rows == 0) {
-                // A filtered materialized batch is still progress; return it 
to Scanner instead of
-                // consuming an unbounded number of position-delete batches in 
this call.
-                block->clear_column_data(_projected_columns.size());
-                *eos = false;
-                return Status::OK();
-            }
             *eos = false;
             return Status::OK();
         }
diff --git a/be/src/format_v2/table/paimon_reader.cpp 
b/be/src/format_v2/table/paimon_reader.cpp
index d815ef81c9b..5d8363848f3 100644
--- a/be/src/format_v2/table/paimon_reader.cpp
+++ b/be/src/format_v2/table/paimon_reader.cpp
@@ -152,27 +152,6 @@ void PaimonHybridReader::set_batch_size(size_t batch_size) 
{
     }
 }
 
-Status PaimonHybridReader::append_conjuncts(const VExprContextSPtrs& 
conjuncts) {
-    // The wrapper snapshot initializes future children, while every existing 
child needs the same
-    // late RF immediately so active and later reused splits keep identical 
predicate ownership.
-    const size_t owned_count =
-            
_appended_table_reader_owned_conjunct_count.value_or(conjuncts.size());
-    RETURN_IF_ERROR(format::TableReader::append_conjuncts(conjuncts));
-    if (_native_reader != nullptr) {
-        
RETURN_IF_ERROR(_native_reader->append_conjuncts_with_ownership(conjuncts, 
owned_count));
-    }
-    if (_jni_reader != nullptr) {
-        
RETURN_IF_ERROR(_jni_reader->append_conjuncts_with_ownership(conjuncts, 
owned_count));
-    }
-    return Status::OK();
-}
-
-const format::MaterializedBlockStats& 
PaimonHybridReader::last_materialized_block_stats() const {
-    // FileScannerV2 budgets cooperative work from the child that actually 
materialized the block.
-    return _current_split_reader != nullptr ? 
_current_split_reader->last_materialized_block_stats()
-                                            : 
format::TableReader::last_materialized_block_stats();
-}
-
 int64_t PaimonHybridReader::condition_cache_hit_count() const {
     // Both children survive split switches, so the wrapper must publish their 
cumulative totals;
     // returning only the active child would make FileScannerV2's monotonic 
delta go backwards.
@@ -227,7 +206,6 @@ Status 
PaimonHybridReader::_init_child_reader(format::TableReader* reader,
     RETURN_IF_ERROR(reader->init({
             .projected_columns = _projected_columns,
             .conjuncts = std::move(conjuncts),
-            .table_reader_owned_conjunct_count = 
_table_reader_owned_conjunct_count,
             .format = file_format,
             .scan_params = _scan_params,
             .io_ctx = _io_ctx,
diff --git a/be/src/format_v2/table/paimon_reader.h 
b/be/src/format_v2/table/paimon_reader.h
index b4f076fc6e4..823fa6540d2 100644
--- a/be/src/format_v2/table/paimon_reader.h
+++ b/be/src/format_v2/table/paimon_reader.h
@@ -71,8 +71,6 @@ public:
     Status abort_split() override;
     Status close() override;
     void set_batch_size(size_t batch_size) override;
-    Status append_conjuncts(const VExprContextSPtrs& conjuncts) override;
-    const format::MaterializedBlockStats& last_materialized_block_stats() 
const override;
     int64_t condition_cache_hit_count() const override;
 
 #ifdef BE_TEST
diff --git a/be/src/format_v2/table_reader.cpp 
b/be/src/format_v2/table_reader.cpp
index a073f01f237..4beaf8c9ff5 100644
--- a/be/src/format_v2/table_reader.cpp
+++ b/be/src/format_v2/table_reader.cpp
@@ -678,8 +678,6 @@ Status TableReader::init(TableReadOptions&& options) {
                 ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, 
"PrepareSplitTime", table_profile, 1);
         _profile.finalize_timer =
                 ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, 
"FinalizeBlockTime", table_profile, 1);
-        _profile.residual_filter_timer = ADD_CHILD_TIMER_WITH_LEVEL(
-                _scanner_profile, "ResidualFilterTime", table_profile, 1);
         _profile.create_reader_timer =
                 ADD_CHILD_TIMER_WITH_LEVEL(_scanner_profile, 
"CreateReaderTime", table_profile, 1);
         _profile.pushdown_agg_timer =
@@ -723,9 +721,6 @@ Status TableReader::init(TableReadOptions&& options) {
     _push_down_count_columns = options.push_down_count_columns;
     _initial_condition_cache_digest = options.condition_cache_digest;
     _condition_cache_digest = _initial_condition_cache_digest;
-    _table_reader_owned_conjunct_count =
-            
options.table_reader_owned_conjunct_count.value_or(options.conjuncts.size());
-    DORIS_CHECK_LE(_table_reader_owned_conjunct_count, 
options.conjuncts.size());
     _projected_columns = std::move(options.projected_columns);
     if (supports_iceberg_scan_semantics_v1(_scan_params)) {
         for (auto& projected_column : _projected_columns) {
@@ -739,64 +734,7 @@ Status TableReader::init(TableReadOptions&& options) {
     }
     _system_properties = create_system_properties(_scan_params);
     _mapper_options.mode = TableColumnMappingMode::BY_NAME;
-    return _replace_conjuncts(options.conjuncts);
-}
-
-Status TableReader::_prepare_conjunct(const VExprContextSPtr& source, 
VExprContextSPtr* prepared) {
-    DORIS_CHECK(source != nullptr);
-    DORIS_CHECK(source->root() != nullptr);
-    DORIS_CHECK(prepared != nullptr);
-    VExprSPtr root;
-    RETURN_IF_ERROR(clone_table_expr_tree(source->root(), &root));
-    auto conjunct = VExprContext::create_shared(std::move(root));
-    RETURN_IF_ERROR(conjunct->prepare(_runtime_state, RowDescriptor {}));
-    RETURN_IF_ERROR(conjunct->open(_runtime_state));
-    *prepared = std::move(conjunct);
-    return Status::OK();
-}
-
-Status TableReader::_replace_conjuncts(const VExprContextSPtrs& conjuncts) {
-    VExprContextSPtrs prepared;
-    prepared.reserve(conjuncts.size());
-    for (const auto& source : conjuncts) {
-        VExprContextSPtr conjunct;
-        RETURN_IF_ERROR(_prepare_conjunct(source, &conjunct));
-        prepared.push_back(std::move(conjunct));
-    }
-    _conjuncts = std::move(prepared);
-    return Status::OK();
-}
-
-Status TableReader::append_conjuncts_with_ownership(const VExprContextSPtrs& 
conjuncts,
-                                                    size_t 
table_reader_owned_conjunct_count) {
-    DORIS_CHECK(!_appended_table_reader_owned_conjunct_count.has_value());
-    _appended_table_reader_owned_conjunct_count = 
table_reader_owned_conjunct_count;
-    auto status = append_conjuncts(conjuncts);
-    _appended_table_reader_owned_conjunct_count.reset();
-    return status;
-}
-
-Status TableReader::append_conjuncts(const VExprContextSPtrs& conjuncts) {
-    const size_t owned_count =
-            
_appended_table_reader_owned_conjunct_count.value_or(conjuncts.size());
-    DORIS_CHECK_LE(owned_count, conjuncts.size());
-    // Once Scanner owns a suffix, later predicates cannot be inserted into 
the TableReader-owned
-    // prefix without reordering them ahead of that stateful/error-preserving 
barrier.
-    DORIS_CHECK(owned_count == 0 || _table_reader_owned_conjunct_count == 
_conjuncts.size());
-    for (size_t conjunct_index = 0; conjunct_index < conjuncts.size(); 
++conjunct_index) {
-        const auto& source = conjuncts[conjunct_index];
-        VExprContextSPtr conjunct;
-        RETURN_IF_ERROR(_prepare_conjunct(source, &conjunct));
-        _conjuncts.push_back(conjunct);
-        if (conjunct_index < owned_count) {
-            ++_table_reader_owned_conjunct_count;
-        }
-        if (_current_task != nullptr && conjunct_index < owned_count) {
-            // The active reader has already fixed its localized predicate 
set. Appended runtime
-            // filters must remain residual until the next split rebuilds its 
FileScanRequest.
-            _remaining_conjuncts.push_back(std::move(conjunct));
-        }
-    }
+    _conjuncts = std::move(options.conjuncts);
     return Status::OK();
 }
 
@@ -804,27 +742,20 @@ Status TableReader::_build_table_filters_from_conjuncts() 
{
     _table_filters.clear();
     _constant_pruning_safe_filter_count = 0;
     bool in_safe_prefix = true;
-    for (size_t conjunct_index = 0; conjunct_index < _conjuncts.size(); 
++conjunct_index) {
-        const auto& conjunct = _conjuncts[conjunct_index];
+    for (const auto& conjunct : _conjuncts) {
         DORIS_CHECK(conjunct != nullptr);
         DORIS_CHECK(conjunct->root() != nullptr);
         // `_table_filters` omits expressions without slot references, but 
such an expression still
         // occupies a position in the row-level conjunct order. Record how 
many localized filters
         // precede the first unsafe original conjunct so constant pruning 
cannot jump over a
-        // slotless non-deterministic/error-preserving barrier. An unsafe 
predicate is either kept
-        // on TableReader's post-materialization path by a standalone caller 
or carried only for
-        // analysis when FileScannerV2 owns the ordered suffix.
-        if (in_safe_prefix && !is_safe_to_pre_execute(conjunct)) {
+        // slotless non-deterministic/error-preserving barrier. Unsafe 
predicates remain solely on
+        // Scanner's original row-level path because localizing a clone would 
execute their state
+        // twice with independent state.
+        if (in_safe_prefix && !_is_safe_to_pre_execute(conjunct)) {
             in_safe_prefix = false;
         }
-        const size_t filters_before = _table_filters.size();
         RETURN_IF_ERROR(
                 build_table_filters_from_conjunct(conjunct, _runtime_state, 
&_table_filters));
-        for (size_t filter_index = filters_before; filter_index < 
_table_filters.size();
-             ++filter_index) {
-            _table_filters[filter_index].source_conjunct_index = 
conjunct_index;
-            _table_filters[filter_index].can_localize = in_safe_prefix;
-        }
         if (in_safe_prefix) {
             _constant_pruning_safe_filter_count = _table_filters.size();
         }
@@ -832,59 +763,6 @@ Status TableReader::_build_table_filters_from_conjuncts() {
     return Status::OK();
 }
 
-Status TableReader::_prepare_all_conjuncts_as_remaining() {
-    // Expression contexts carry mutable state (for example sequence/stateful 
functions). Select
-    // from the TableReader-owned contexts instead of reopening clones for 
every split.
-    _remaining_conjuncts.assign(
-            _conjuncts.begin(),
-            _conjuncts.begin() + 
cast_set<ptrdiff_t>(_table_reader_owned_conjunct_count));
-    return Status::OK();
-}
-
-Status TableReader::_prepare_remaining_conjuncts(
-        const FilterLocalizationResult& localization_result) {
-    DORIS_CHECK(localization_result.localized_filters.size() == 
_table_filters.size());
-    std::vector<bool> localized_conjuncts(_conjuncts.size(), false);
-    for (size_t filter_index = 0; filter_index < _table_filters.size(); 
++filter_index) {
-        if (!localization_result.localized_filters[filter_index]) {
-            continue;
-        }
-        const size_t source_index = 
_table_filters[filter_index].source_conjunct_index;
-        DORIS_CHECK(source_index < localized_conjuncts.size());
-        localized_conjuncts[source_index] = true;
-    }
-
-    _remaining_conjuncts.clear();
-    for (size_t conjunct_index = 0; conjunct_index < 
_table_reader_owned_conjunct_count;
-         ++conjunct_index) {
-        if (localized_conjuncts[conjunct_index]) {
-            continue;
-        }
-        _remaining_conjuncts.push_back(_conjuncts[conjunct_index]);
-    }
-    return Status::OK();
-}
-
-Status TableReader::_filter_remaining_conjuncts(Block* block, size_t* rows) {
-    DORIS_CHECK(block != nullptr);
-    DORIS_CHECK(rows != nullptr);
-    if (*rows == 0 || _remaining_conjuncts.empty()) {
-        return Status::OK();
-    }
-    SCOPED_TIMER(_profile.residual_filter_timer);
-    const size_t rows_before_filter = *rows;
-    auto status = VExprContext::filter_block(_remaining_conjuncts, block, 
block->columns());
-    if (!status.ok() && _format == FileFormat::ORC) {
-        status.prepend("Orc row reader nextBatch failed. reason = ");
-    }
-    RETURN_IF_ERROR(status);
-    *rows = block->columns() == 0 ? rows_before_filter : block->rows();
-    if (_io_ctx != nullptr) {
-        _io_ctx->predicate_filtered_rows += rows_before_filter - *rows;
-    }
-    return Status::OK();
-}
-
 Status TableReader::_open_local_filter_exprs(const FileScanRequest& 
file_request) {
     RowDescriptor row_desc;
     for (const auto& conjunct : file_request.conjuncts) {
@@ -1125,9 +1003,6 @@ Status TableReader::prepare_split(const SplitReadOptions& 
options) {
     SCOPED_TIMER(_profile.total_timer);
     SCOPED_TIMER(_profile.prepare_split_timer);
     _current_split_pruned = false;
-    // Predicate localization belongs to the physical schema of one split. 
Clear the previous
-    // ownership before any early return so a pruned or failed split cannot 
leak it to the next one.
-    _remaining_conjuncts.clear();
     _all_runtime_filters_applied_for_split = 
options.all_runtime_filters_applied;
     _condition_cache_digest_covers_current_split = 
options.condition_cache_digest.has_value();
     if (options.condition_cache_digest.has_value()) {
@@ -1141,7 +1016,7 @@ Status TableReader::prepare_split(const SplitReadOptions& 
options) {
         _condition_cache_digest = _initial_condition_cache_digest;
     }
     if (options.conjuncts.has_value()) {
-        RETURN_IF_ERROR(_replace_conjuncts(*options.conjuncts));
+        _conjuncts = *options.conjuncts;
     }
     // Update to current split format to handle ORC/PARQUET files in one table.
     _format = options.current_split_format;
@@ -1211,7 +1086,7 @@ Status 
TableReader::_evaluate_partition_prune_conjuncts(const VExprContextSPtrs&
         // Keep only the safe prefix of the original conjunct order. If an 
unsafe conjunct is
         // skipped, a later predicate could prune the split before the unsafe 
one reaches its
         // normal row-level evaluation point.
-        if (!is_safe_to_pre_execute(conjunct)) {
+        if (!_is_safe_to_pre_execute(conjunct)) {
             break;
         }
         std::set<GlobalIndex> global_indices;
@@ -1247,7 +1122,7 @@ Status 
TableReader::_evaluate_partition_prune_conjuncts(const VExprContextSPtrs&
                                            can_filter_all);
 }
 
-bool TableReader::is_safe_to_pre_execute(const VExprContextSPtr& conjunct) {
+bool TableReader::_is_safe_to_pre_execute(const VExprContextSPtr& conjunct) {
     DORIS_CHECK(conjunct != nullptr);
     DORIS_CHECK(conjunct->root() != nullptr);
     const auto root = conjunct->root();
diff --git a/be/src/format_v2/table_reader.h b/be/src/format_v2/table_reader.h
index 48e3d996ce2..ea40280ee99 100644
--- a/be/src/format_v2/table_reader.h
+++ b/be/src/format_v2/table_reader.h
@@ -78,14 +78,10 @@ namespace doris::format {
 using DeleteRows = std::vector<int64_t>;
 
 // Row-level predicates on table/global schema. They are rewritten to 
file-local expressions when
-// possible; otherwise TableReader evaluates them after final table-schema 
materialization.
+// possible, and remain the source of row-level filtering after localization.
 struct TableFilter {
     VExprContextSPtr conjunct;
     std::vector<GlobalIndex> global_indices;
-    size_t source_conjunct_index = 0;
-    // False after the first unsafe source conjunct so file-local execution 
cannot reorder a later
-    // predicate ahead of stateful or error-preserving table semantics.
-    bool can_localize = true;
 };
 
 struct ScanTask {
@@ -116,7 +112,6 @@ struct ReadProfile {
     RuntimeProfile::Counter* exec_timer = nullptr;
     RuntimeProfile::Counter* prepare_split_timer = nullptr;
     RuntimeProfile::Counter* finalize_timer = nullptr;
-    RuntimeProfile::Counter* residual_filter_timer = nullptr;
     RuntimeProfile::Counter* create_reader_timer = nullptr;
     RuntimeProfile::Counter* pushdown_agg_timer = nullptr;
     RuntimeProfile::Counter* open_reader_timer = nullptr;
@@ -133,23 +128,12 @@ struct ReadProfile {
     RuntimeProfile::Counter* file_reader_close_timer = nullptr;
 };
 
-struct MaterializedBlockStats {
-    bool has_materialized_input = false;
-    size_t rows = 0;
-    size_t bytes = 0;
-    size_t allocated_bytes = 0;
-};
-
 struct TableReadOptions {
     // Columns need to be read from file and output by table reader. They are 
all in table/global
     // schema semantics.
     const std::vector<ColumnDefinition> projected_columns;
     // All complex conjuncts from scan operator
     const VExprContextSPtrs conjuncts;
-    // Number of leading conjuncts whose row-level execution is owned by 
TableReader/FileReader.
-    // FileScannerV2 still passes the complete ordered list so mapping, 
pruning guards, aggregate
-    // eligibility, and condition-cache analysis see the exact query 
semantics. nullopt means all.
-    const std::optional<size_t> table_reader_owned_conjunct_count = 
std::nullopt;
     // File format of the underlying data files, needed for reader 
initialization and reader-level
     // filter pushdown.
     const FileFormat format;
@@ -222,10 +206,6 @@ public:
 
 #ifdef BE_TEST
     size_t TEST_batch_size() const { return _batch_size; }
-    size_t TEST_conjunct_count() const { return _conjuncts.size(); }
-    size_t TEST_table_reader_owned_conjunct_count() const {
-        return _table_reader_owned_conjunct_count;
-    }
     void TEST_set_condition_cache_hit_count(int64_t hits) { 
_condition_cache_hit_count = hits; }
     bool TEST_current_data_file_is_immutable() const {
         DORIS_CHECK(_current_task != nullptr);
@@ -247,25 +227,6 @@ public:
         return _current_split_uses_metadata_count;
     }
 
-    // Runtime filters that arrive after a split has opened cannot be pushed 
into that file reader.
-    // Keep their expression contexts in TableReader and evaluate them as 
residual predicates for
-    // the active reader; later splits can localize them normally.
-    virtual Status append_conjuncts(const VExprContextSPtrs& conjuncts);
-
-    // Append a full ordered snapshot delta while marking only its leading 
prefix as owned by
-    // TableReader/FileReader. This non-virtual wrapper preserves the 
long-standing virtual API and
-    // carries the ownership boundary through hybrid readers to their children.
-    Status append_conjuncts_with_ownership(const VExprContextSPtrs& conjuncts,
-                                           size_t 
table_reader_owned_conjunct_count);
-
-    // Shared safety classification for deciding which ordered conjunct prefix 
may execute below
-    // Scanner without changing stateful or error-preserving semantics.
-    static bool is_safe_to_pre_execute(const VExprContextSPtr& conjunct);
-
-    virtual const MaterializedBlockStats& last_materialized_block_stats() 
const {
-        return _last_materialized_block_stats;
-    }
-
     // Discard the active split after the caller decides an error is 
ignorable, for example a
     // stale external-table file listing that returns NOT_FOUND. The next 
prepare_split() must start
     // with no concrete reader or split-local state left from the failed split.
@@ -285,7 +246,6 @@ public:
         _remaining_file_level_count = -1;
         _current_split_uses_metadata_count = false;
         _current_split_pruned = false;
-        _remaining_conjuncts.clear();
         return Status::OK();
     }
 
@@ -295,7 +255,6 @@ public:
     virtual Status get_block(Block* block, bool* eos) {
         SCOPED_TIMER(_profile.total_timer);
         SCOPED_TIMER(_profile.exec_timer);
-        _last_materialized_block_stats = {};
         DORIS_CHECK(block->columns() == _projected_columns.size());
         block->clear_column_data(_projected_columns.size());
 
@@ -368,7 +327,7 @@ public:
             RETURN_IF_ERROR(_check_file_block_columns("after file reader 
get_block", current_rows));
 #endif
             DORIS_CHECK(block->columns() == 
_data_reader.column_mapper->mappings().size());
-            RETURN_IF_ERROR(finalize_chunk(block, &current_rows));
+            RETURN_IF_ERROR(finalize_chunk(block, current_rows));
 #ifndef NDEBUG
             RETURN_IF_ERROR(
                     _check_table_block_columns("after finalize_chunk", block, 
current_rows));
@@ -377,13 +336,6 @@ public:
                 _current_reader_reached_eof = !stopped_during_read;
                 RETURN_IF_ERROR(close_current_reader());
             }
-            if (current_rows == 0) {
-                // One materialized batch is one Scanner progress unit even 
when residual
-                // predicates reject every row. Returning here preserves 
row-budget and
-                // cancellation checks in Scanner::get_block().
-                block->clear_column_data(_projected_columns.size());
-                return Status::OK();
-            }
             return Status::OK();
         }
     }
@@ -401,7 +353,6 @@ public:
         _remaining_table_level_count = -1;
         _remaining_file_level_count = -1;
         _current_split_uses_metadata_count = false;
-        _remaining_conjuncts.clear();
         return Status::OK();
     }
 
@@ -487,17 +438,14 @@ protected:
         // reader with the request. File scan request carries row-level 
expression filters and
         // file-level pruning hints. Only expression filters decide returned 
rows.
         auto file_request = std::make_shared<FileScanRequest>();
-        FilterLocalizationResult localization_result;
         RETURN_IF_ERROR(_data_reader.column_mapper->create_scan_request(
-                _table_filters, _projected_columns, file_request.get(), 
_runtime_state,
-                &localization_result));
+                _table_filters, _projected_columns, file_request.get(), 
_runtime_state));
         bool constant_filter_pruned_split = false;
         
RETURN_IF_ERROR(_evaluate_constant_filters(&constant_filter_pruned_split));
         if (constant_filter_pruned_split) {
             RETURN_IF_ERROR(close_current_reader());
             return Status::OK();
         }
-        RETURN_IF_ERROR(_prepare_remaining_conjuncts(localization_result));
         // COUNT(*) has no semantic column argument, but Nereids retains a 
minimum-width scan slot
         // so the scan node still has an output tuple. Record only the current 
non-predicate file
         // columns before table-format hooks add row-position or 
equality-delete dependencies. This
@@ -573,13 +521,9 @@ protected:
     }
 
     Status _build_table_filters_from_conjuncts();
-    Status _replace_conjuncts(const VExprContextSPtrs& conjuncts);
-    Status _prepare_conjunct(const VExprContextSPtr& source, VExprContextSPtr* 
prepared);
-    Status _prepare_remaining_conjuncts(const FilterLocalizationResult& 
localization_result);
-    Status _prepare_all_conjuncts_as_remaining();
-    Status _filter_remaining_conjuncts(Block* block, size_t* rows);
     Status _evaluate_partition_prune_conjuncts(const VExprContextSPtrs& 
conjuncts,
                                                bool* can_filter_all);
+    static bool _is_safe_to_pre_execute(const VExprContextSPtr& conjunct);
     Status _build_partition_prune_block(Block* block) const;
     Status _open_local_filter_exprs(const FileScanRequest& file_request);
     Status _init_reader_condition_cache(const FileScanRequest& file_request);
@@ -598,7 +542,7 @@ protected:
             if (table_filter.conjunct == nullptr) {
                 continue;
             }
-            DORIS_CHECK(is_safe_to_pre_execute(table_filter.conjunct));
+            DORIS_CHECK(_is_safe_to_pre_execute(table_filter.conjunct));
             // RuntimeFilterExpr does not implement execute_column_impl(); it 
is evaluated by the
             // row-level filter path through execute_filter(). Constant split 
pruning uses
             // VExprContext::execute() on a one-row synthetic block, so 
runtime filters must not be
@@ -815,7 +759,6 @@ protected:
         }
         _table_filters.clear();
         _constant_pruning_safe_filter_count = 0;
-        _remaining_conjuncts.clear();
         _data_reader.file_schema.clear();
         _data_reader.file_block_layout.clear();
         _data_reader.block_template.clear();
@@ -831,28 +774,15 @@ protected:
         }
     }
 
-    void _reset_materialized_block_stats() { _last_materialized_block_stats = 
{}; }
-
-    void _record_materialized_block_stats(const Block& block, size_t rows) {
-        _last_materialized_block_stats = {
-                .has_materialized_input = true,
-                .rows = rows,
-                .bytes = block.bytes(),
-                .allocated_bytes = block.allocated_bytes(),
-        };
-    }
-
     // Finalize file-local block to table/global schema block.
-    Status finalize_chunk(Block* block, size_t* rows) {
-        DORIS_CHECK(rows != nullptr);
+    Status finalize_chunk(Block* block, const size_t rows) {
         SCOPED_TIMER(_profile.finalize_timer);
         size_t idx = 0;
         const auto& mappings = _data_reader.column_mapper->mappings();
         for (const auto& mapping : mappings) {
             ColumnPtr column;
-            RETURN_IF_ERROR(_materialize_mapping_column(mapping, 
&_data_reader.block_template,
-                                                        *rows, &column,
-                                                        idx + 1 == 
mappings.size()));
+            RETURN_IF_ERROR(_materialize_mapping_column(mapping, 
&_data_reader.block_template, rows,
+                                                        &column, idx + 1 == 
mappings.size()));
             block->replace_by_position(idx, 
IColumn::mutate(std::move(column)));
             idx++;
         }
@@ -860,12 +790,7 @@ protected:
         // Enforce CHAR/VARCHAR length declared by the table schema after all 
file-to-table
         // materialization has finished.
         RETURN_IF_ERROR(_truncate_char_or_varchar_columns(block));
-        // Preserve the cost of materialization before residual predicates 
shrink the block. The
-        // scanner uses this snapshot for bounded progress and adaptive batch 
sizing.
-        _record_materialized_block_stats(*block, *rows);
-        // Predicate ownership is split-local: only predicates not 
acknowledged as exact by this
-        // split's FileScanRequest run here, after 
virtual/default/schema-evolution values exist.
-        return _filter_remaining_conjuncts(block, rows);
+        return Status::OK();
     }
 
     // Materialize virtual columns in the table block, such as Iceberg _row_id 
and
@@ -1029,7 +954,31 @@ protected:
     // - table VARCHAR(10), file STRING: truncate to 10 because STRING has no 
declared bound;
     // - table STRING, any file type: no truncation because the target has no 
bound.
     static bool _should_truncate_char_or_varchar_column(const ColumnMapping& 
mapping) {
-        return requires_char_or_varchar_truncation(mapping);
+        if (mapping.table_type == nullptr) {
+            return false;
+        }
+        const auto table_type = remove_nullable(mapping.table_type);
+        const auto primitive_type = table_type->get_primitive_type();
+        if (primitive_type != TYPE_VARCHAR && primitive_type != TYPE_CHAR) {
+            return false;
+        }
+        const auto target_len = assert_cast<const 
DataTypeString*>(table_type.get())->len();
+        if (target_len <= 0) {
+            return false;
+        }
+        if (mapping.file_type == nullptr) {
+            return true;
+        }
+        const auto file_type = remove_nullable(mapping.file_type);
+        DORIS_CHECK(file_type != nullptr);
+        int file_len = -1;
+        if (file_type->get_primitive_type() == TYPE_VARCHAR ||
+            file_type->get_primitive_type() == TYPE_CHAR ||
+            file_type->get_primitive_type() == TYPE_STRING) {
+            file_len = assert_cast<const 
DataTypeString*>(file_type.get())->len();
+        }
+
+        return file_len < 0 || target_len < file_len;
     }
 
     // Truncate a materialized CHAR/VARCHAR column in place by reusing the 
vectorized substring
@@ -1126,8 +1075,9 @@ protected:
         if (!_all_runtime_filters_applied_for_split) {
             return false;
         }
-        // Even a slotless conjunct that cannot become a TableFilter must see 
every source row
-        // before an aggregate reduces the stream to synthetic COUNT/MINMAX 
rows.
+        // Scanner owns the original conjunct list and evaluates it after 
TableReader finalizes
+        // rows. Even a slotless conjunct that cannot become a TableFilter 
must see every source
+        // row before an aggregate reduces the stream to synthetic 
COUNT/MINMAX rows.
         if (!_conjuncts.empty()) {
             return false;
         }
@@ -1799,10 +1749,6 @@ protected:
     // intentionally absent from that vector but must still act as ordering 
barriers.
     size_t _constant_pruning_safe_filter_count = 0;
     VExprContextSPtrs _conjuncts;
-    size_t _table_reader_owned_conjunct_count = 0;
-    std::optional<size_t> _appended_table_reader_owned_conjunct_count;
-    VExprContextSPtrs _remaining_conjuncts;
-    MaterializedBlockStats _last_materialized_block_stats;
     ReadProfile _profile;
     // Parsed from row-position based delete files, including position delete 
and deletion vector.
     DeleteRows* _delete_rows = nullptr;
diff --git a/be/src/storage/segment/adaptive_block_size_predictor.cpp 
b/be/src/storage/segment/adaptive_block_size_predictor.cpp
index 7a5ad573a2e..d8cc700f579 100644
--- a/be/src/storage/segment/adaptive_block_size_predictor.cpp
+++ b/be/src/storage/segment/adaptive_block_size_predictor.cpp
@@ -32,14 +32,11 @@ 
AdaptiveBlockSizePredictor::AdaptiveBlockSizePredictor(size_t preferred_block_si
           _metadata_hint_bytes_per_row(metadata_hint_bytes_per_row) {}
 
 void AdaptiveBlockSizePredictor::update(const Block& block) {
-    update(block.rows(), block.bytes());
-}
-
-void AdaptiveBlockSizePredictor::update(size_t rows, size_t bytes) {
+    size_t rows = block.rows();
     if (rows == 0) {
         return;
     }
-    double cur = static_cast<double>(bytes) / static_cast<double>(rows);
+    double cur = static_cast<double>(block.bytes()) / 
static_cast<double>(rows);
 
     if (!_has_history) {
         _bytes_per_row = cur;
diff --git a/be/src/storage/segment/adaptive_block_size_predictor.h 
b/be/src/storage/segment/adaptive_block_size_predictor.h
index f327fec8517..e03f18c2a53 100644
--- a/be/src/storage/segment/adaptive_block_size_predictor.h
+++ b/be/src/storage/segment/adaptive_block_size_predictor.h
@@ -60,7 +60,6 @@ public:
     // Update EWMA estimates from a completed batch.  Must be called only when 
block.rows() > 0
     // and the batch returned Status::OK().
     void update(const Block& block);
-    void update(size_t rows, size_t bytes);
 
     // Predict how many rows the next batch should read.
     // Never exceeds |block_size_rows|; never returns less than 1.
diff --git a/be/test/exec/scan/file_scanner_v2_test.cpp 
b/be/test/exec/scan/file_scanner_v2_test.cpp
index e1a2fd0cf88..87d4c92a776 100644
--- a/be/test/exec/scan/file_scanner_v2_test.cpp
+++ b/be/test/exec/scan/file_scanner_v2_test.cpp
@@ -749,6 +749,18 @@ TEST(FileScannerV2Test, EndOfFileIsSkippedAsEmptySplit) {
     EXPECT_FALSE(FileScannerV2::TEST_should_skip_empty(Status::OK(), false));
 }
 
+TEST(FileScannerV2Test, OrcScannerResidualFilterRetainsNextBatchContext) {
+    auto status = FileScannerV2::TEST_contextualize_output_filter_status(
+            Status::InvalidArgument("synthetic row filter failure"), 
TFileFormatType::FORMAT_ORC);
+    EXPECT_NE(status.to_string().find("nextBatch failed"), std::string::npos) 
<< status;
+    EXPECT_NE(status.to_string().find("synthetic row filter failure"), 
std::string::npos) << status;
+
+    status = FileScannerV2::TEST_contextualize_output_filter_status(
+            Status::InvalidArgument("synthetic row filter failure"),
+            TFileFormatType::FORMAT_PARQUET);
+    EXPECT_EQ(status.to_string().find("nextBatch failed"), std::string::npos) 
<< status;
+}
+
 // Scenario: partition slots are identified from the explicit FE category when 
present, otherwise
 // from the legacy is_file_slot flag. Scanner-generated rowid columns must 
never be treated as
 // partition columns even if FE marks them as non-file slots.
@@ -888,27 +900,4 @@ TEST(FileScannerTest, 
PartitionPruningStopsAtUnsafePredicate) {
     EXPECT_EQ(partition_conjuncts[0], conjuncts[0]);
 }
 
-TEST(FileScannerV2Test, ScannerOwnsUnsafeConjunctAndOrderedSuffixInProfile) {
-    const auto bool_type = std::make_shared<DataTypeUInt8>();
-    auto unsafe_predicate = std::make_shared<UnsafePartitionPredicate>();
-    unsafe_predicate->add_child(slot_ref(1, 0, bool_type, "part"));
-    VExprContextSPtrs conjuncts {
-            runtime_filter_context(slot_ref(1, 0, bool_type, "part"), 1),
-            runtime_filter_context(std::move(unsafe_predicate), 2),
-            runtime_filter_context(slot_ref(1, 0, bool_type, "part"), 3),
-    };
-
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    RuntimeProfile profile("file_scanner_v2");
-    FileScannerV2 scanner(&state, &profile, nullptr);
-    scanner.TEST_set_scanner_conjuncts(std::move(conjuncts));
-
-    EXPECT_EQ(scanner.TEST_table_reader_owned_conjunct_count(), 1);
-    EXPECT_EQ(scanner.TEST_scanner_residual_conjunct_count(), 2);
-    const auto* residual_predicates = 
profile.get_info_string("ScannerResidualPredicates");
-    ASSERT_NE(residual_predicates, nullptr);
-    EXPECT_FALSE(residual_predicates->empty());
-    EXPECT_NE(residual_predicates->find("SlotRef"), std::string::npos) << 
*residual_predicates;
-}
-
 } // namespace doris
diff --git a/be/test/exec/scan/scanner_late_arrival_rf_test.cpp 
b/be/test/exec/scan/scanner_late_arrival_rf_test.cpp
index b70f1cf9ea0..0d31b694951 100644
--- a/be/test/exec/scan/scanner_late_arrival_rf_test.cpp
+++ b/be/test/exec/scan/scanner_late_arrival_rf_test.cpp
@@ -65,23 +65,6 @@ private:
     std::list<Block> _blocks;
 };
 
-class HighCostPredicate final : public VExpr {
-public:
-    HighCostPredicate() : VExpr(std::make_shared<DataTypeUInt8>(), false) {}
-
-    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t count,
-                               ColumnPtr& result_column) const override {
-        result_column = ColumnUInt8::create(count, 1);
-        return Status::OK();
-    }
-
-    const std::string& expr_name() const override { return _expr_name; }
-    double execute_cost() const override { return 100.0; }
-
-private:
-    const std::string _expr_name = "high_cost_stateful_predicate";
-};
-
 class ScannerLateArrivalRfTest : public RuntimeFilterTest {
 public:
     void SetUp() override {
@@ -100,7 +83,6 @@ public:
 // the counter advances after RFs arrive and that the second call 
short-circuits
 // via the fast path at the top of the function.
 TEST_F(ScannerLateArrivalRfTest, applied_rf_num_advances_after_late_arrival) {
-    
_runtime_states[0]->_query_options.__set_enable_adjust_conjunct_order_by_cost(true);
     std::vector<TRuntimeFilterDesc> rf_descs = {
             TRuntimeFilterDescBuilder().add_planId_to_target_expr(0).build(),
             TRuntimeFilterDescBuilder().add_planId_to_target_expr(0).build()};
@@ -122,55 +104,26 @@ TEST_F(ScannerLateArrivalRfTest, 
applied_rf_num_advances_after_late_arrival) {
 
     auto local_state = 
std::make_shared<MockScanLocalState>(_runtime_states[0].get(), op.get());
 
-    auto initial_conjunct = 
VExprContext::create_shared(std::make_shared<HighCostPredicate>());
-    ASSERT_TRUE(initial_conjunct->prepare(_runtime_states[0].get(), 
row_desc).ok());
-    ASSERT_TRUE(initial_conjunct->open(_runtime_states[0].get()).ok());
-    local_state->_conjuncts.push_back(initial_conjunct);
-
     std::vector<std::shared_ptr<Dependency>> rf_dependencies;
     ASSERT_TRUE(local_state->_helper.init(_runtime_states[0].get(), true, 0, 
0, rf_dependencies, "")
                         .ok());
 
-    std::shared_ptr<RuntimeFilterProducer> producer;
-    ASSERT_TRUE(RuntimeFilterProducer::create(_query_ctx.get(), 
rf_descs.data(), &producer).ok());
-    
producer->set_wrapper_state_and_ready_to_publish(RuntimeFilterWrapper::State::READY);
-    local_state->_helper._consumers[0]->signal(producer.get());
-    ASSERT_TRUE(local_state->_helper
-                        .acquire_runtime_filter(_runtime_states[0].get(), 
local_state->_conjuncts,
-                                                row_desc)
-                        .ok());
-    ASSERT_EQ(local_state->_conjuncts.size(), 2);
-
     auto scanner = std::make_unique<TestScanner>(_runtime_states[0].get(), 
local_state.get(),
                                                  -1 /*limit*/, &_profile);
-    ASSERT_TRUE(scanner->init(_runtime_states[0].get(), 
local_state->_conjuncts).ok());
-    auto second_scanner = 
std::make_unique<TestScanner>(_runtime_states[0].get(), local_state.get(),
-                                                        -1 /*limit*/, 
&_profile);
-    ASSERT_TRUE(second_scanner->init(_runtime_states[0].get(), 
local_state->_conjuncts).ok());
+    ASSERT_TRUE(scanner->init(_runtime_states[0].get(), {}).ok());
     ASSERT_EQ(scanner->_total_rf_num, 2);
     ASSERT_EQ(scanner->_applied_rf_num, 0);
 
+    std::shared_ptr<RuntimeFilterProducer> producer;
+    ASSERT_TRUE(RuntimeFilterProducer::create(_query_ctx.get(), 
rf_descs.data(), &producer).ok());
+    
producer->set_wrapper_state_and_ready_to_publish(RuntimeFilterWrapper::State::READY);
+    local_state->_helper._consumers[0]->signal(producer.get());
     local_state->_helper._consumers[1]->signal(producer.get());
 
     // First call after both RFs arrived: counter must advance to total. Before
     // the fix this stayed at 0 because the assignment was missing.
     ASSERT_TRUE(scanner->try_append_late_arrival_runtime_filter().ok());
     ASSERT_EQ(scanner->_applied_rf_num, 2);
-    ASSERT_EQ(scanner->_late_arrival_rf_conjuncts.size(), 1);
-    for (const auto& conjunct : scanner->_late_arrival_rf_conjuncts) {
-        EXPECT_NE(dynamic_cast<const 
RuntimeFilterExpr*>(conjunct->root().get()), nullptr);
-    }
-    ASSERT_EQ(scanner->_conjuncts.size(), 3);
-    EXPECT_EQ(scanner->_conjuncts.back()->expr_name(), 
"high_cost_stateful_predicate");
-
-    // The first scanner consumes the shared helper's expression, so another 
scanner can only get
-    // the exact delta from the local state's append-only RF batch history.
-    ASSERT_TRUE(second_scanner->try_append_late_arrival_runtime_filter().ok());
-    ASSERT_EQ(second_scanner->_applied_rf_num, 2);
-    ASSERT_EQ(second_scanner->_late_arrival_rf_conjuncts.size(), 1);
-    EXPECT_NE(dynamic_cast<const RuntimeFilterExpr*>(
-                      
second_scanner->_late_arrival_rf_conjuncts[0]->root().get()),
-              nullptr);
 
     // Second call: must hit the fast-path early return without re-cloning.
     // We clear `_conjuncts` and verify the function does NOT repopulate them;
diff --git 
a/be/test/format/table/iceberg/iceberg_position_delete_sys_table_reader_test.cpp
 
b/be/test/format/table/iceberg/iceberg_position_delete_sys_table_reader_test.cpp
index e22060e4417..b453e7d32b1 100644
--- 
a/be/test/format/table/iceberg/iceberg_position_delete_sys_table_reader_test.cpp
+++ 
b/be/test/format/table/iceberg/iceberg_position_delete_sys_table_reader_test.cpp
@@ -35,12 +35,10 @@
 #include "core/column/column_nullable.h"
 #include "core/column/column_string.h"
 #include "core/column/column_struct.h"
-#include "core/column/column_vector.h"
 #include "core/data_type/data_type_nullable.h"
 #include "core/data_type/data_type_number.h"
 #include "core/data_type/data_type_string.h"
 #include "core/data_type/data_type_struct.h"
-#include "exprs/vexpr.h"
 #include "format/orc/orc_memory_stream_test.h"
 #include "format/table/iceberg_scan_semantics.h"
 #include "format/table/parquet_utils.h"
@@ -67,31 +65,6 @@ protected:
     void _collect_profile_before_close() override { ++collect_calls; }
 };
 
-class RejectAllRowsPredicate final : public VExpr {
-public:
-    RejectAllRowsPredicate() : VExpr(std::make_shared<DataTypeUInt8>(), false) 
{}
-
-    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t count,
-                               ColumnPtr& result_column) const override {
-        auto result = ColumnUInt8::create();
-        result->get_data().resize_fill(count, 0);
-        result_column = std::move(result);
-        return Status::OK();
-    }
-
-    const std::string& expr_name() const override { return _name; }
-    bool is_deterministic() const override { return false; }
-
-    Status clone_node(VExprSPtr* cloned_expr) const override {
-        DORIS_CHECK(cloned_expr != nullptr);
-        *cloned_expr = std::make_shared<RejectAllRowsPredicate>();
-        return Status::OK();
-    }
-
-private:
-    const std::string _name = "RejectAllRowsPredicate";
-};
-
 SlotDescriptor* make_slot(ObjectPool* pool, int id, std::string name, 
DataTypePtr type) {
     TSlotDescriptor slot_desc;
     slot_desc.__set_id(id);
@@ -1028,52 +1001,6 @@ TEST(IcebergPositionDeleteSysTableV2ReaderTest, 
StopsBeforeExpandingDeletionVect
     EXPECT_TRUE(eof);
 }
 
-TEST(IcebergPositionDeleteSysTableV2ReaderTest,
-     AllFilteredDeletionVectorYieldsBeforeObservingCancellation) {
-    ObjectPool pool;
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    RuntimeProfile profile("test_profile");
-    const auto nullable_int64 = 
make_nullable(std::make_shared<DataTypeInt64>());
-    std::vector<SlotDescriptor*> file_slot_descs {
-            make_slot(&pool, 0, "pos", nullable_int64),
-    };
-
-    auto conjunct = 
VExprContext::create_shared(std::make_shared<RejectAllRowsPredicate>());
-    RowDescriptor row_desc;
-    ASSERT_TRUE(conjunct->prepare(&state, row_desc).ok());
-    ASSERT_TRUE(conjunct->open(&state).ok());
-
-    format::iceberg::IcebergPositionDeleteSysTableV2Reader reader;
-    reader._runtime_state = &state;
-    reader._scanner_profile = &profile;
-    reader._io_ctx = std::make_shared<io::IOContext>();
-    reader._file_slot_descs = &file_slot_descs;
-    reader._projected_columns.resize(file_slot_descs.size());
-    reader._remaining_conjuncts = {conjunct};
-    reader._has_split = true;
-    reader._delete_file_kind =
-            
format::iceberg::IcebergPositionDeleteSysTableV2Reader::DeleteFileKind::DELETION_VECTOR;
-    reader._batch_size = 1;
-    reader._dv_positions.add(uint64_t {7});
-    reader._dv_positions.add(uint64_t {9});
-    reader._dv_positions.add(uint64_t {11});
-    reader._next_dv_position.emplace(reader._dv_positions.begin());
-
-    Block block = make_output_block(file_slot_descs);
-    bool eof = false;
-    ASSERT_TRUE(reader.get_block(&block, &eof).ok());
-    EXPECT_FALSE(eof);
-    EXPECT_EQ(block.rows(), 0);
-    ASSERT_TRUE(reader._next_dv_position.has_value());
-    EXPECT_EQ(**reader._next_dv_position, 9);
-
-    reader._io_ctx->should_stop = true;
-    ASSERT_TRUE(reader.get_block(&block, &eof).ok());
-    EXPECT_TRUE(eof);
-    ASSERT_TRUE(reader._next_dv_position.has_value());
-    EXPECT_EQ(**reader._next_dv_position, 9);
-}
-
 TEST(IcebergPositionDeleteSysTableV2ReaderTest, 
ParquetRowUsesAnyFieldIdMapping) {
     run_mixed_id_position_delete_test(format::FileFormat::PARQUET, 
TFileFormatType::FORMAT_PARQUET,
                                       "parquet");
diff --git a/be/test/format_v2/column_mapper_test.cpp 
b/be/test/format_v2/column_mapper_test.cpp
index d3428b6134a..29d4efbe17f 100644
--- a/be/test/format_v2/column_mapper_test.cpp
+++ b/be/test/format_v2/column_mapper_test.cpp
@@ -2337,39 +2337,6 @@ TEST(ColumnMapperLocalizeFiltersTest, 
VisibleLocalFilterAddsPredicateColumnAndCo
     EXPECT_TRUE(localized_slot->data_type()->equals(*int_type));
 }
 
-TEST(ColumnMapperLocalizeFiltersTest, ReportsLocalizationForEachSplitMapping) {
-    const auto int_type = i32();
-    auto table_column = name_col("id", int_type);
-    const std::vector<ColumnDefinition> table_schema = {table_column};
-    TableFilter filter {
-            .conjunct = VExprContext::create_shared(int_gt(table_slot(0, 0, 
int_type, "id"), 1)),
-            .global_indices = {GlobalIndex(0)}};
-
-    TableColumnMapper local_mapper({.mode = TableColumnMappingMode::BY_NAME});
-    ASSERT_TRUE(local_mapper.create_mapping(table_schema, {}, {name_col("id", 
int_type, 7)}).ok());
-    FileScanRequest local_request;
-    FilterLocalizationResult local_result;
-    ASSERT_TRUE(local_mapper
-                        .create_scan_request({filter}, table_schema, 
&local_request, nullptr,
-                                             &local_result)
-                        .ok());
-    ASSERT_EQ(local_result.localized_filters.size(), 1);
-    EXPECT_TRUE(local_result.localized_filters[0]);
-    ASSERT_EQ(local_request.conjuncts.size(), 1);
-
-    TableColumnMapper missing_mapper({.mode = 
TableColumnMappingMode::BY_NAME});
-    ASSERT_TRUE(missing_mapper.create_mapping(table_schema, {}, {}).ok());
-    FileScanRequest missing_request;
-    FilterLocalizationResult missing_result;
-    ASSERT_TRUE(missing_mapper
-                        .create_scan_request({filter}, table_schema, 
&missing_request, nullptr,
-                                             &missing_result)
-                        .ok());
-    ASSERT_EQ(missing_result.localized_filters.size(), 1);
-    EXPECT_FALSE(missing_result.localized_filters[0]);
-    EXPECT_TRUE(missing_request.conjuncts.empty());
-}
-
 TEST(ColumnMapperLocalizeFiltersTest, VarbinaryFilterStaysAboveFileReader) {
     const auto binary_type = varbinary();
     const auto table_column = name_col("partition_key", binary_type);
@@ -2395,35 +2362,6 @@ TEST(ColumnMapperLocalizeFiltersTest, 
VarbinaryFilterStaysAboveFileReader) {
     EXPECT_TRUE(request.conjuncts.empty());
 }
 
-TEST(ColumnMapperLocalizeFiltersTest, 
VarcharWidthTruncationFilterStaysAboveFileReader) {
-    const auto table_type = std::make_shared<DataTypeString>(3, TYPE_VARCHAR);
-    const auto file_type = std::make_shared<DataTypeString>(10, TYPE_VARCHAR);
-    const auto table_column = name_col("value", table_type);
-    const auto file_column = name_col("value", file_type, 7);
-
-    TableColumnMapper mapper({.mode = TableColumnMappingMode::BY_NAME});
-    ASSERT_TRUE(mapper.create_mapping({table_column}, {}, {file_column}).ok());
-
-    TableFilter filter {.conjunct = 
VExprContext::create_shared(binary_predicate(
-                                TExprOpcode::EQ, table_slot(0, 0, table_type, 
"value"),
-                                literal(table_type, 
Field::create_field<TYPE_STRING>("abc")))),
-                        .global_indices = {GlobalIndex(0)}};
-    TQueryOptions query_options;
-    query_options.__set_truncate_char_or_varchar_columns(true);
-    RuntimeState state {query_options, TQueryGlobals()};
-    FileScanRequest request;
-    FilterLocalizationResult localization_result;
-
-    ASSERT_TRUE(mapper.create_scan_request({filter}, {table_column}, &request, 
&state,
-                                           &localization_result)
-                        .ok());
-    ASSERT_EQ(localization_result.localized_filters.size(), 1);
-    EXPECT_FALSE(localization_result.localized_filters[0]);
-    EXPECT_TRUE(request.conjuncts.empty());
-    ASSERT_EQ(request.non_predicate_columns.size(), 1);
-    EXPECT_EQ(request.non_predicate_columns[0].column_id(), LocalColumnId(7));
-}
-
 TEST(ColumnMapperLocalizeFiltersTest, 
NestedVarbinaryFilterStaysAboveFileReader) {
     const auto table_column = struct_name_col(
             "payload", {name_col("id", i32()), name_col("binary_value", 
varbinary())});
@@ -2574,7 +2512,7 @@ TEST(ColumnMapperScanRequestTest, 
HiddenTopLevelFilterMappingUsesNameFallback) {
     EXPECT_EQ(mapper.filter_entries().at(GlobalIndex(1)).local_index(), 
LocalIndex(1));
 }
 
-TEST(ColumnMapperScanRequestTest, OrdinaryPredicateSlotRetainsOutputPayload) {
+TEST(ColumnMapperScanRequestTest, 
OrdinaryPredicateSlotRetainsPayloadForScannerBoundary) {
     const auto int_type = i32();
     auto quantity = name_col("ss_quantity", int_type);
     auto tax = name_col("ss_ext_tax", int_type);
@@ -2599,8 +2537,8 @@ TEST(ColumnMapperScanRequestTest, 
OrdinaryPredicateSlotRetainsOutputPayload) {
     EXPECT_EQ(request.predicate_columns[0].column_id(), LocalColumnId(0));
     ASSERT_EQ(request.non_predicate_columns.size(), 1);
     EXPECT_EQ(request.non_predicate_columns[0].column_id(), LocalColumnId(1));
-    // A visible predicate slot is still part of the table output and cannot 
be replaced with a
-    // default-valued placeholder after file-local filtering.
+    // The scanner evaluates its table-level conjuncts after TableReader 
returns, so a visible
+    // predicate slot cannot be replaced with a default-valued placeholder at 
the file boundary.
     EXPECT_TRUE(request.predicate_only_columns.empty());
 }
 
diff --git a/be/test/format_v2/table/hudi_reader_test.cpp 
b/be/test/format_v2/table/hudi_reader_test.cpp
index f5bd70292e5..96126281744 100644
--- a/be/test/format_v2/table/hudi_reader_test.cpp
+++ b/be/test/format_v2/table/hudi_reader_test.cpp
@@ -37,9 +37,6 @@
 #include "core/data_type/data_type_string.h"
 #include "core/data_type/data_type_struct.h"
 #include "core/field.h"
-#include "exec/scan/file_scanner_v2.h"
-#include "exprs/vexpr_context.h"
-#include "exprs/vliteral.h"
 #include "format_v2/column_data.h"
 #include "gen_cpp/ExternalTableSchema_types.h"
 #include "gen_cpp/PlanNodes_types.h"
@@ -144,54 +141,6 @@ public:
     }
 };
 
-class AppendTrackingTableReader final : public TableReader {
-public:
-    Status append_conjuncts(const VExprContextSPtrs& conjuncts) override {
-        appended_conjuncts += conjuncts.size();
-        owned_conjuncts += 
_appended_table_reader_owned_conjunct_count.value_or(conjuncts.size());
-        return Status::OK();
-    }
-
-    size_t appended_conjuncts = 0;
-    size_t owned_conjuncts = 0;
-};
-
-class OneRowTableReader final : public TableReader {
-public:
-    Status prepare_split(const SplitReadOptions&) override { return 
Status::OK(); }
-
-    Status get_block(Block* block, bool* eos) override {
-        auto column = ColumnInt32::create();
-        column->insert_value(1);
-        block->replace_by_position(0, std::move(column));
-        *eos = false;
-        return Status::OK();
-    }
-};
-
-class StatefulHybridPredicate final : public VExpr {
-public:
-    explicit StatefulHybridPredicate(std::vector<int>* observed_invocations)
-            : VExpr(std::make_shared<DataTypeUInt8>(), false),
-              _observed_invocations(observed_invocations) {}
-
-    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t count,
-                               ColumnPtr& result_column) const override {
-        _observed_invocations->push_back(_invocation++);
-        result_column = ColumnUInt8::create(count, 1);
-        return Status::OK();
-    }
-
-    const std::string& expr_name() const override { return _expr_name; }
-    bool is_constant() const override { return false; }
-    bool is_deterministic() const override { return false; }
-
-private:
-    std::vector<int>* const _observed_invocations;
-    mutable int _invocation = 0;
-    const std::string _expr_name = "StatefulHybridPredicate";
-};
-
 // Scenario: FileScannerV2 Hudi native reader uses the split schema id to 
annotate the physical
 // file schema before TableColumnMapper runs. This keeps schema-evolved Hudi 
files on field-id
 // mapping, including renamed nested children.
@@ -313,102 +262,6 @@ TEST(HudiHybridReaderTest, 
AdaptiveBatchSizeReachesBothChildReaders) {
     EXPECT_EQ(child_batch_sizes.second, 123);
 }
 
-TEST(HudiHybridReaderTest, ReportsActiveChildMaterializedBlockStats) {
-    hudi::HudiHybridReader reader;
-    reader.TEST_install_batch_size_children();
-    reader._current_split_reader = reader._native_reader.get();
-    reader._native_reader->_last_materialized_block_stats = {
-            .has_materialized_input = true, .rows = 7, .bytes = 70, 
.allocated_bytes = 96};
-
-    const auto& stats = reader.last_materialized_block_stats();
-    EXPECT_TRUE(stats.has_materialized_input);
-    EXPECT_EQ(stats.rows, 7);
-    EXPECT_EQ(stats.bytes, 70);
-    EXPECT_EQ(stats.allocated_bytes, 96);
-}
-
-TEST(HudiHybridReaderTest, LateConjunctReachesInitializedNativeAndJniChildren) 
{
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    hudi::HudiHybridReader reader;
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = {},
-                                    .conjuncts = {},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = nullptr,
-                            })
-                        .ok());
-    auto native_reader = std::make_unique<AppendTrackingTableReader>();
-    auto jni_reader = std::make_unique<AppendTrackingTableReader>();
-    auto* native_reader_ptr = native_reader.get();
-    auto* jni_reader_ptr = jni_reader.get();
-    reader._native_reader = std::move(native_reader);
-    reader._jni_reader = std::move(jni_reader);
-
-    auto literal = VLiteral::create_shared(std::make_shared<DataTypeInt32>(),
-                                           Field::create_field<TYPE_INT>(1));
-    ASSERT_TRUE(reader.append_conjuncts_with_ownership(
-                              
{VExprContext::create_shared(std::move(literal))}, 0)
-                        .ok());
-    EXPECT_EQ(native_reader_ptr->appended_conjuncts, 1);
-    EXPECT_EQ(jni_reader_ptr->appended_conjuncts, 1);
-    EXPECT_EQ(native_reader_ptr->owned_conjuncts, 0);
-    EXPECT_EQ(jni_reader_ptr->owned_conjuncts, 0);
-}
-
-TEST(HudiHybridReaderTest, 
ScannerStatefulResidualSurvivesNativeJniNativeSwitch) {
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    RuntimeProfile profile("hudi_scanner_stateful_residual");
-    TFileScanRangeParams scan_params;
-    scan_params.__set_format_type(TFileFormatType::FORMAT_PARQUET);
-    auto hybrid_reader = std::make_unique<hudi::HudiHybridReader>();
-    auto* hybrid_reader_ptr = hybrid_reader.get();
-    hybrid_reader_ptr->TEST_set_child_reader_factories(
-            [] { return std::make_unique<OneRowTableReader>(); },
-            [] { return std::make_unique<OneRowTableReader>(); });
-
-    std::vector<int> observed_invocations;
-    auto conjunct = VExprContext::create_shared(
-            std::make_shared<StatefulHybridPredicate>(&observed_invocations));
-    ASSERT_TRUE(conjunct->prepare(&state, RowDescriptor {}).ok());
-    ASSERT_TRUE(conjunct->open(&state).ok());
-    FileScannerV2 scanner(&state, &profile, std::move(hybrid_reader));
-    scanner.TEST_set_scanner_conjuncts({std::move(conjunct)});
-
-    const std::vector<ColumnDefinition> projected_columns {
-            make_table_column(0, "id", std::make_shared<DataTypeInt32>()),
-    };
-    ASSERT_TRUE(hybrid_reader_ptr
-                        ->init({
-                                .projected_columns = projected_columns,
-                                .conjuncts = {},
-                                .format = FileFormat::PARQUET,
-                                .scan_params = &scan_params,
-                                .io_ctx = nullptr,
-                                .runtime_state = &state,
-                                .scanner_profile = &profile,
-                        })
-                        .ok());
-
-    auto run_split = [&](FileFormat format, TFileFormatType::type 
thrift_format) {
-        SplitReadOptions split;
-        split.current_split_format = format;
-        split.current_range.__set_format_type(thrift_format);
-        ASSERT_TRUE(hybrid_reader_ptr->prepare_split(split).ok());
-        Block block = build_table_block(projected_columns);
-        bool eos = false;
-        ASSERT_TRUE(hybrid_reader_ptr->get_block(&block, &eos).ok());
-        ASSERT_TRUE(scanner.TEST_filter_output_block(&block).ok());
-    };
-    run_split(FileFormat::PARQUET, TFileFormatType::FORMAT_PARQUET);
-    run_split(FileFormat::JNI, TFileFormatType::FORMAT_JNI);
-    run_split(FileFormat::PARQUET, TFileFormatType::FORMAT_PARQUET);
-
-    EXPECT_EQ(observed_invocations, std::vector<int>({0, 1, 2}));
-}
-
 TEST(HudiHybridReaderTest, AggregatesConditionCacheHitsFromBothChildren) {
     hudi::HudiHybridReader reader;
     reader.TEST_install_batch_size_children();
diff --git a/be/test/format_v2/table/iceberg_reader_test.cpp 
b/be/test/format_v2/table/iceberg_reader_test.cpp
index c42d68933f2..782463e335a 100644
--- a/be/test/format_v2/table/iceberg_reader_test.cpp
+++ b/be/test/format_v2/table/iceberg_reader_test.cpp
@@ -1146,6 +1146,11 @@ VExprContextSPtr prepared_conjunct(RuntimeState* state, 
const VExprSPtr& expr) {
     return ctx;
 }
 
+void apply_final_conjuncts(Block* block, const VExprContextSPtrs& conjuncts) {
+    const auto status = VExprContext::filter_block(conjuncts, block, 
block->columns());
+    ASSERT_TRUE(status.ok()) << status;
+}
+
 TEST(IcebergV2ReaderTest, IcebergVirtualColumnsUseRowLineageMetadata) {
     const auto test_dir =
             std::filesystem::temp_directory_path() / 
"doris_iceberg_virtual_columns_test";
@@ -1384,6 +1389,9 @@ TEST(IcebergV2ReaderTest, 
IcebergRowIdPredicateFiltersAfterRowLineageMaterializa
     bool eos = false;
     ASSERT_TRUE(reader.get_block(&block, &eos).ok());
     ASSERT_FALSE(eos);
+    ASSERT_EQ(block.rows(), 3);
+
+    apply_final_conjuncts(&block, conjuncts);
     ASSERT_EQ(block.rows(), 1);
     expect_nullable_int64_column_values(*block.get_by_position(0).column, 
{1001});
     expect_nullable_int64_column_values(*block.get_by_position(1).column, 
{77});
@@ -1435,6 +1443,9 @@ TEST(IcebergV2ReaderTest, 
IcebergLastUpdatedSequencePredicateFiltersAfterMateria
     bool eos = false;
     ASSERT_TRUE(reader.get_block(&block, &eos).ok());
     ASSERT_FALSE(eos);
+    ASSERT_EQ(block.rows(), 3);
+
+    apply_final_conjuncts(&block, conjuncts);
     ASSERT_EQ(block.rows(), 1);
     expect_nullable_int64_column_values(*block.get_by_position(0).column, 
{1001});
     expect_nullable_int64_column_values(*block.get_by_position(1).column, 
{77});
diff --git a/be/test/format_v2/table/paimon_reader_test.cpp 
b/be/test/format_v2/table/paimon_reader_test.cpp
index 77466439c6b..4186aa78f03 100644
--- a/be/test/format_v2/table/paimon_reader_test.cpp
+++ b/be/test/format_v2/table/paimon_reader_test.cpp
@@ -45,9 +45,6 @@
 #include "core/data_type/data_type_string.h"
 #include "core/field.h"
 #include "exec/common/endian.h"
-#include "exec/scan/file_scanner_v2.h"
-#include "exprs/vexpr_context.h"
-#include "exprs/vliteral.h"
 #include "format/format_common.h"
 #include "format/table/deletion_vector_reader.h"
 #include "format/table/paimon_reader.h"
@@ -82,54 +79,6 @@ public:
     }
 };
 
-class AppendTrackingTableReader final : public TableReader {
-public:
-    Status append_conjuncts(const VExprContextSPtrs& conjuncts) override {
-        appended_conjuncts += conjuncts.size();
-        owned_conjuncts += 
_appended_table_reader_owned_conjunct_count.value_or(conjuncts.size());
-        return Status::OK();
-    }
-
-    size_t appended_conjuncts = 0;
-    size_t owned_conjuncts = 0;
-};
-
-class OneRowTableReader final : public TableReader {
-public:
-    Status prepare_split(const SplitReadOptions&) override { return 
Status::OK(); }
-
-    Status get_block(Block* block, bool* eos) override {
-        auto column = ColumnInt32::create();
-        column->insert_value(1);
-        block->replace_by_position(0, std::move(column));
-        *eos = false;
-        return Status::OK();
-    }
-};
-
-class StatefulHybridPredicate final : public VExpr {
-public:
-    explicit StatefulHybridPredicate(std::vector<int>* observed_invocations)
-            : VExpr(std::make_shared<DataTypeUInt8>(), false),
-              _observed_invocations(observed_invocations) {}
-
-    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t count,
-                               ColumnPtr& result_column) const override {
-        _observed_invocations->push_back(_invocation++);
-        result_column = ColumnUInt8::create(count, 1);
-        return Status::OK();
-    }
-
-    const std::string& expr_name() const override { return _expr_name; }
-    bool is_constant() const override { return false; }
-    bool is_deterministic() const override { return false; }
-
-private:
-    std::vector<int>* const _observed_invocations;
-    mutable int _invocation = 0;
-    const std::string _expr_name = "StatefulHybridPredicate";
-};
-
 DataTypePtr table_type(const DataTypePtr& type) {
     return type->is_nullable() ? type : make_nullable(type);
 }
@@ -745,101 +694,6 @@ TEST(PaimonHybridReaderTest, 
AdaptiveBatchSizeReachesBothChildReaders) {
     EXPECT_EQ(child_batch_sizes.second, 321);
 }
 
-TEST(PaimonHybridReaderTest, ReportsActiveChildMaterializedBlockStats) {
-    paimon::PaimonHybridReader reader;
-    reader.TEST_install_batch_size_children();
-    reader._current_split_reader = reader._native_reader.get();
-    reader._native_reader->_last_materialized_block_stats = {
-            .has_materialized_input = true, .rows = 7, .bytes = 70, 
.allocated_bytes = 96};
-
-    const auto& stats = reader.last_materialized_block_stats();
-    EXPECT_TRUE(stats.has_materialized_input);
-    EXPECT_EQ(stats.rows, 7);
-    EXPECT_EQ(stats.bytes, 70);
-    EXPECT_EQ(stats.allocated_bytes, 96);
-}
-
-TEST(PaimonHybridReaderTest, 
LateConjunctReachesInitializedNativeAndJniChildren) {
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    paimon::PaimonHybridReader reader;
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = {},
-                                    .conjuncts = {},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = nullptr,
-                            })
-                        .ok());
-    auto native_reader = std::make_unique<AppendTrackingTableReader>();
-    auto jni_reader = std::make_unique<AppendTrackingTableReader>();
-    auto* native_reader_ptr = native_reader.get();
-    auto* jni_reader_ptr = jni_reader.get();
-    reader._native_reader = std::move(native_reader);
-    reader._jni_reader = std::move(jni_reader);
-
-    auto literal = VLiteral::create_shared(std::make_shared<DataTypeInt32>(),
-                                           Field::create_field<TYPE_INT>(1));
-    ASSERT_TRUE(reader.append_conjuncts_with_ownership(
-                              
{VExprContext::create_shared(std::move(literal))}, 0)
-                        .ok());
-    EXPECT_EQ(native_reader_ptr->appended_conjuncts, 1);
-    EXPECT_EQ(jni_reader_ptr->appended_conjuncts, 1);
-    EXPECT_EQ(native_reader_ptr->owned_conjuncts, 0);
-    EXPECT_EQ(jni_reader_ptr->owned_conjuncts, 0);
-}
-
-TEST(PaimonHybridReaderTest, 
ScannerStatefulResidualSurvivesNativeJniNativeSwitch) {
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    RuntimeProfile profile("paimon_scanner_stateful_residual");
-    auto scan_params = make_local_parquet_scan_params();
-    auto hybrid_reader = std::make_unique<paimon::PaimonHybridReader>();
-    auto* hybrid_reader_ptr = hybrid_reader.get();
-    hybrid_reader_ptr->TEST_set_child_reader_factories(
-            [] { return std::make_unique<OneRowTableReader>(); },
-            [] { return std::make_unique<OneRowTableReader>(); });
-
-    std::vector<int> observed_invocations;
-    auto conjunct = VExprContext::create_shared(
-            std::make_shared<StatefulHybridPredicate>(&observed_invocations));
-    ASSERT_TRUE(conjunct->prepare(&state, RowDescriptor {}).ok());
-    ASSERT_TRUE(conjunct->open(&state).ok());
-    FileScannerV2 scanner(&state, &profile, std::move(hybrid_reader));
-    scanner.TEST_set_scanner_conjuncts({std::move(conjunct)});
-
-    const std::vector<ColumnDefinition> projected_columns {
-            make_table_column(0, "id", std::make_shared<DataTypeInt32>()),
-    };
-    ASSERT_TRUE(hybrid_reader_ptr
-                        ->init({
-                                .projected_columns = projected_columns,
-                                .conjuncts = {},
-                                .format = FileFormat::PARQUET,
-                                .scan_params = &scan_params,
-                                .io_ctx = nullptr,
-                                .runtime_state = &state,
-                                .scanner_profile = &profile,
-                        })
-                        .ok());
-
-    auto run_split = [&](FileFormat format, TFileRangeDesc range) {
-        SplitReadOptions split;
-        split.current_split_format = format;
-        split.current_range = std::move(range);
-        ASSERT_TRUE(hybrid_reader_ptr->prepare_split(split).ok());
-        Block block = build_table_block(projected_columns);
-        bool eos = false;
-        ASSERT_TRUE(hybrid_reader_ptr->get_block(&block, &eos).ok());
-        ASSERT_TRUE(scanner.TEST_filter_output_block(&block).ok());
-    };
-    run_split(FileFormat::PARQUET, 
make_paimon_native_range(TFileFormatType::FORMAT_PARQUET));
-    run_split(FileFormat::JNI, make_paimon_jni_range());
-    run_split(FileFormat::PARQUET, 
make_paimon_native_range(TFileFormatType::FORMAT_PARQUET));
-
-    EXPECT_EQ(observed_invocations, std::vector<int>({0, 1, 2}));
-}
-
 TEST(PaimonHybridReaderTest, AggregatesConditionCacheHitsFromBothChildren) {
     paimon::PaimonHybridReader reader;
     reader.TEST_install_batch_size_children();
diff --git a/be/test/format_v2/table_reader_test.cpp 
b/be/test/format_v2/table_reader_test.cpp
index d92bbf2b0ac..48b38d78e19 100644
--- a/be/test/format_v2/table_reader_test.cpp
+++ b/be/test/format_v2/table_reader_test.cpp
@@ -297,37 +297,6 @@ private:
     const std::string _expr_name = "NonDeterministicPartitionPredicate";
 };
 
-class StatefulSequencePredicate final : public VExpr {
-public:
-    explicit StatefulSequencePredicate(std::vector<int>* observed_invocations)
-            : VExpr(std::make_shared<DataTypeUInt8>(), false),
-              _observed_invocations(observed_invocations) {}
-
-    Status execute_column_impl(VExprContext*, const Block*, const Selector*, 
size_t count,
-                               ColumnPtr& result_column) const override {
-        DORIS_CHECK(_observed_invocations != nullptr);
-        _observed_invocations->push_back(_invocation++);
-        auto result = ColumnUInt8::create();
-        result->get_data().resize_fill(count, 1);
-        result_column = std::move(result);
-        return Status::OK();
-    }
-
-    const std::string& expr_name() const override { return _expr_name; }
-    bool is_deterministic() const override { return false; }
-
-    Status clone_node(VExprSPtr* cloned_expr) const override {
-        DORIS_CHECK(cloned_expr != nullptr);
-        *cloned_expr = 
std::make_shared<StatefulSequencePredicate>(_observed_invocations);
-        return Status::OK();
-    }
-
-private:
-    std::vector<int>* const _observed_invocations;
-    mutable int _invocation = 0;
-    const std::string _expr_name = "StatefulSequencePredicate";
-};
-
 class NullableArrayBigintDefaultExpr final : public VExpr {
 public:
     explicit NullableArrayBigintDefaultExpr(DataTypePtr data_type)
@@ -565,23 +534,6 @@ void write_parquet_file(const std::string& file_path, 
int32_t id, const std::str
                                                       builder.build()));
 }
 
-void write_single_int_parquet_file(const std::string& file_path, const 
std::string& column_name,
-                                   int32_t value) {
-    auto schema = arrow::schema({arrow::field(column_name, arrow::int32(), 
false)});
-    auto table = arrow::Table::Make(schema, {build_int32_array({value})});
-
-    auto file_result = arrow::io::FileOutputStream::Open(file_path);
-    ASSERT_TRUE(file_result.ok()) << file_result.status();
-    std::shared_ptr<arrow::io::FileOutputStream> out = *file_result;
-
-    ::parquet::WriterProperties::Builder builder;
-    builder.version(::parquet::ParquetVersion::PARQUET_2_6);
-    builder.data_page_version(::parquet::ParquetDataPageVersion::V2);
-    builder.compression(::parquet::Compression::UNCOMPRESSED);
-    PARQUET_THROW_NOT_OK(::parquet::arrow::WriteTable(*table, 
arrow::default_memory_pool(), out, 1,
-                                                      builder.build()));
-}
-
 void write_struct_parquet_file(const std::string& file_path, int32_t id) {
     auto struct_type = arrow::struct_({arrow::field("id", arrow::int32(), 
false)});
     arrow::StructBuilder builder(
@@ -1103,8 +1055,6 @@ struct FakeFileReaderState {
     bool stop_during_aggregate = false;
     bool stop_during_read = false;
     bool not_found_during_init = false;
-    int batch_count = 1;
-    int get_block_count = 0;
     std::shared_ptr<FileScanRequest> last_request;
     std::optional<FileAggregateRequest> last_aggregate_request;
     std::shared_ptr<ConditionCacheContext> condition_cache_ctx;
@@ -1143,7 +1093,7 @@ public:
         RETURN_IF_ERROR(FileReader::open(std::move(request)));
         _state->last_request = _request;
         ++_state->open_count;
-        _returned_batches = 0;
+        _returned_batch = false;
         return Status::OK();
     }
 
@@ -1152,8 +1102,7 @@ public:
         DORIS_CHECK(rows != nullptr);
         DORIS_CHECK(eof != nullptr);
         DORIS_CHECK(_request != nullptr);
-        ++_state->get_block_count;
-        if (_returned_batches >= _state->batch_count) {
+        if (_returned_batch) {
             *rows = 0;
             *eof = true;
             return Status::OK();
@@ -1200,9 +1149,9 @@ public:
             DORIS_CHECK(_state->io_ctx != nullptr);
             _state->io_ctx->should_stop = true;
         }
-        ++_returned_batches;
+        _returned_batch = true;
         *rows = 2;
-        *eof = _state->eof_with_first_batch && _returned_batches >= 
_state->batch_count;
+        *eof = _state->eof_with_first_batch;
         if (_state->condition_cache_ctx != nullptr && 
!_state->condition_cache_ctx->is_hit &&
             _state->condition_cache_ctx->filter_result != nullptr &&
             !_state->condition_cache_ctx->filter_result->empty()) {
@@ -1258,7 +1207,7 @@ public:
 private:
     std::vector<ColumnDefinition> _schema;
     std::shared_ptr<FakeFileReaderState> _state;
-    int _returned_batches = 0;
+    bool _returned_batch = false;
 };
 
 class FakeTableReader final : public TableReader {
@@ -1432,54 +1381,13 @@ TEST(TableReaderTest, 
ConstantPruningStopsAtUnsafePredicate) {
     Block block = build_table_block(projected_columns);
     bool eos = false;
     ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    EXPECT_TRUE(predicate_executed);
+    EXPECT_FALSE(predicate_executed);
     EXPECT_FALSE(eos);
-    // The file was still opened, proving constant pruning did not jump over 
the unsafe predicate;
-    // the predicate is evaluated only after the resulting table row is 
materialized.
     EXPECT_EQ(fake_state->open_count, 1);
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    EXPECT_TRUE(eos);
-    ASSERT_TRUE(reader.close().ok());
-}
-
-TEST(TableReaderTest, UnsafePredicateRunsAfterTableMaterialization) {
-    std::vector<ColumnDefinition> file_schema;
-    file_schema.push_back(make_file_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    std::vector<ColumnDefinition> projected_columns;
-    projected_columns.push_back(make_table_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    set_name_identifiers(&projected_columns);
-
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    bool predicate_executed = false;
-    auto unsafe_predicate =
-            
std::make_shared<NonDeterministicPartitionPredicate>(&predicate_executed);
-    unsafe_predicate->add_child(table_int32_slot_ref(0, 0, "id"));
-    auto fake_state = std::make_shared<FakeFileReaderState>();
-    FakeTableReader reader(file_schema, fake_state);
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = projected_columns,
-                                    .conjuncts = {prepared_conjunct(&state, 
unsafe_predicate)},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = nullptr,
-                            })
-                        .ok());
-
-    SplitReadOptions split;
-    split.current_range.__set_path("fake-table-reader-input");
-    ASSERT_TRUE(reader.prepare_split(split).ok());
-    Block block = build_table_block(projected_columns);
-    bool eos = false;
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    ASSERT_NE(fake_state->last_request, nullptr);
-    EXPECT_TRUE(fake_state->last_request->conjuncts.empty());
-    EXPECT_TRUE(predicate_executed);
     ASSERT_TRUE(reader.close().ok());
 }
 
-TEST(TableReaderTest, 
ScannerOwnedUnsafePredicateIsPassedButNotExecutedByTableReader) {
+TEST(TableReaderTest, UnsafePredicateStaysOnScannerPath) {
     std::vector<ColumnDefinition> file_schema;
     file_schema.push_back(make_file_column(0, "id", 
std::make_shared<DataTypeInt32>()));
     std::vector<ColumnDefinition> projected_columns;
@@ -1496,7 +1404,6 @@ TEST(TableReaderTest, 
ScannerOwnedUnsafePredicateIsPassedButNotExecutedByTableRe
     ASSERT_TRUE(reader.init({
                                     .projected_columns = projected_columns,
                                     .conjuncts = {prepared_conjunct(&state, 
unsafe_predicate)},
-                                    .table_reader_owned_conjunct_count = 0,
                                     .format = FileFormat::PARQUET,
                                     .scan_params = nullptr,
                                     .io_ctx = nullptr,
@@ -1511,178 +1418,9 @@ TEST(TableReaderTest, 
ScannerOwnedUnsafePredicateIsPassedButNotExecutedByTableRe
     Block block = build_table_block(projected_columns);
     bool eos = false;
     ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-
     ASSERT_NE(fake_state->last_request, nullptr);
     EXPECT_TRUE(fake_state->last_request->conjuncts.empty());
-    EXPECT_EQ(reader.TEST_conjunct_count(), 1);
-    EXPECT_EQ(reader.TEST_table_reader_owned_conjunct_count(), 0);
     EXPECT_FALSE(predicate_executed);
-    EXPECT_EQ(block.rows(), 2);
-    ASSERT_TRUE(reader.close().ok());
-}
-
-TEST(TableReaderTest, ResidualExpressionStateSurvivesAcrossSplits) {
-    std::vector<ColumnDefinition> file_schema;
-    file_schema.push_back(make_file_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    std::vector<ColumnDefinition> projected_columns;
-    projected_columns.push_back(make_table_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    set_name_identifiers(&projected_columns);
-
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    std::vector<int> observed_invocations;
-    auto stateful_predicate = 
std::make_shared<StatefulSequencePredicate>(&observed_invocations);
-    stateful_predicate->add_child(table_int32_slot_ref(0, 0, "id"));
-    auto fake_state = std::make_shared<FakeFileReaderState>();
-    FakeTableReader reader(file_schema, fake_state);
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = projected_columns,
-                                    .conjuncts = {prepared_conjunct(&state, 
stateful_predicate)},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = nullptr,
-                            })
-                        .ok());
-
-    SplitReadOptions split;
-    split.current_range.__set_path("fake-table-reader-input");
-    for (int split_index = 0; split_index < 2; ++split_index) {
-        ASSERT_TRUE(reader.prepare_split(split).ok());
-        Block block = build_table_block(projected_columns);
-        bool eos = false;
-        ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-        EXPECT_EQ(block.rows(), 2);
-        ASSERT_TRUE(reader.close().ok());
-    }
-
-    EXPECT_EQ(observed_invocations, std::vector<int>({0, 1}));
-}
-
-TEST(TableReaderTest, AllFilteredResidualReturnsAfterOneMaterializedBatch) {
-    std::vector<ColumnDefinition> file_schema;
-    file_schema.push_back(make_file_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    std::vector<ColumnDefinition> projected_columns;
-    projected_columns.push_back(make_table_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    set_name_identifiers(&projected_columns);
-
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    bool predicate_executed = false;
-    auto fake_state = std::make_shared<FakeFileReaderState>();
-    fake_state->batch_count = 2;
-    FakeTableReader reader(file_schema, fake_state);
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = projected_columns,
-                                    .conjuncts = {prepared_conjunct(
-                                            &state,
-                                            
std::make_shared<NonDeterministicPartitionPredicate>(
-                                                    &predicate_executed))},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = nullptr,
-                            })
-                        .ok());
-
-    SplitReadOptions split;
-    split.current_range.__set_path("fake-table-reader-input");
-    ASSERT_TRUE(reader.prepare_split(split).ok());
-    Block block = build_table_block(projected_columns);
-    bool eos = false;
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-
-    EXPECT_TRUE(predicate_executed);
-    EXPECT_EQ(block.rows(), 0);
-    EXPECT_FALSE(eos);
-    EXPECT_EQ(fake_state->get_block_count, 1);
-    EXPECT_EQ(fake_state->close_count, 0);
-    EXPECT_TRUE(reader.last_materialized_block_stats().has_materialized_input);
-    EXPECT_EQ(reader.last_materialized_block_stats().rows, 2);
-    EXPECT_GT(reader.last_materialized_block_stats().bytes, 0);
-    EXPECT_GT(reader.last_materialized_block_stats().allocated_bytes, 0);
-    ASSERT_TRUE(reader.close().ok());
-}
-
-TEST(TableReaderTest, LateConjunctFiltersAlreadyOpenSplit) {
-    std::vector<ColumnDefinition> file_schema;
-    file_schema.push_back(make_file_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    std::vector<ColumnDefinition> projected_columns;
-    projected_columns.push_back(make_table_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    set_name_identifiers(&projected_columns);
-
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    auto fake_state = std::make_shared<FakeFileReaderState>();
-    fake_state->batch_count = 2;
-    FakeTableReader reader(file_schema, fake_state);
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = projected_columns,
-                                    .conjuncts = {},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = nullptr,
-                            })
-                        .ok());
-
-    SplitReadOptions split;
-    split.current_range.__set_path("fake-table-reader-input");
-    ASSERT_TRUE(reader.prepare_split(split).ok());
-    Block block = build_table_block(projected_columns);
-    bool eos = false;
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    ASSERT_EQ(block.rows(), 2);
-
-    bool predicate_executed = false;
-    auto late_predicate = 
std::make_shared<NonDeterministicPartitionPredicate>(&predicate_executed);
-    late_predicate->add_child(table_int32_slot_ref(0, 0, "id"));
-    ASSERT_TRUE(
-            
reader.append_conjuncts({VExprContext::create_shared(std::move(late_predicate))}).ok());
-    block = build_table_block(projected_columns);
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    EXPECT_TRUE(predicate_executed);
-    EXPECT_EQ(block.rows(), 0);
-    EXPECT_EQ(fake_state->get_block_count, 2);
-    ASSERT_TRUE(reader.close().ok());
-}
-
-TEST(TableReaderTest, ResidualFilteringHasDedicatedProfileTimer) {
-    std::vector<ColumnDefinition> file_schema;
-    file_schema.push_back(make_file_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    std::vector<ColumnDefinition> projected_columns;
-    projected_columns.push_back(make_table_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    set_name_identifiers(&projected_columns);
-
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    RuntimeProfile profile("scanner");
-    bool predicate_executed = false;
-    auto fake_state = std::make_shared<FakeFileReaderState>();
-    FakeTableReader reader(file_schema, fake_state);
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = projected_columns,
-                                    .conjuncts = {prepared_conjunct(
-                                            &state,
-                                            
std::make_shared<NonDeterministicPartitionPredicate>(
-                                                    &predicate_executed))},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = &profile,
-                            })
-                        .ok());
-
-    SplitReadOptions split;
-    split.current_range.__set_path("fake-table-reader-input");
-    ASSERT_TRUE(reader.prepare_split(split).ok());
-    Block block = build_table_block(projected_columns);
-    bool eos = false;
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-
-    EXPECT_TRUE(predicate_executed);
-    ASSERT_NE(profile.get_counter("ResidualFilterTime"), nullptr);
-    EXPECT_GT(profile.get_counter("ResidualFilterTime")->value(), 0);
     ASSERT_TRUE(reader.close().ok());
 }
 
@@ -1733,11 +1471,9 @@ TEST(TableReaderTest, 
ConstantPruningStopsAtUnsafeSlotlessPredicate) {
     EXPECT_EQ(fake_state->open_count, 1);
     ASSERT_NE(fake_state->last_request, nullptr);
     // A slotless unsafe conjunct is an ordering barrier even though it has no 
TableFilter entry.
-    // The later predicate must stay on the post-materialization path instead 
of running inside the
+    // The later predicate must stay on the scanner's row-level path instead 
of running inside the
     // file reader before the unsafe conjunct.
     EXPECT_TRUE(fake_state->last_request->conjuncts.empty());
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    EXPECT_TRUE(eos);
     ASSERT_TRUE(reader.close().ok());
 }
 
@@ -2020,13 +1756,8 @@ TEST(TableReaderTest, 
SlotlessConjunctDisablesAggregatePushdown) {
     // presence still prevents the fake aggregate count (3) from replacing the 
two physical rows.
     ASSERT_NE(fake_state->last_request, nullptr);
     EXPECT_TRUE(fake_state->last_request->conjuncts.empty());
-    // The two physical rows are then filtered at the table boundary, where 
slotless predicates are
-    // evaluated exactly even though they cannot be localized to a file column.
-    EXPECT_EQ(block.rows(), 0);
-    EXPECT_FALSE(eos);
+    EXPECT_EQ(block.rows(), 2);
     EXPECT_TRUE(predicate_executed);
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    EXPECT_TRUE(eos);
     ASSERT_TRUE(reader.close().ok());
 }
 
@@ -4575,59 +4306,6 @@ TEST(TableReaderTest, VExprPredicateSurvivesReopenSplit) 
{
     std::filesystem::remove_all(test_dir);
 }
 
-TEST(TableReaderTest, RecomputesPredicateExecutionLayerForEverySplit) {
-    const auto test_dir = std::filesystem::temp_directory_path() /
-                          "doris_table_reader_split_local_predicate_test";
-    std::filesystem::remove_all(test_dir);
-    std::filesystem::create_directories(test_dir);
-
-    const auto local_file = (test_dir / "local.parquet").string();
-    const auto missing_file = (test_dir / "missing.parquet").string();
-    write_single_int_parquet_file(local_file, "id", 3);
-    write_single_int_parquet_file(missing_file, "other", 9);
-
-    std::vector<ColumnDefinition> projected_columns;
-    projected_columns.push_back(make_table_column(0, "id", 
std::make_shared<DataTypeInt32>()));
-    set_name_identifiers(&projected_columns);
-
-    RuntimeState state {TQueryOptions(), TQueryGlobals()};
-    TableReader reader;
-    ASSERT_TRUE(reader.init({
-                                    .projected_columns = projected_columns,
-                                    .conjuncts = {prepared_conjunct(
-                                            &state, 
table_int32_greater_than_expr(0, 0, 2))},
-                                    .format = FileFormat::PARQUET,
-                                    .scan_params = nullptr,
-                                    .io_ctx = nullptr,
-                                    .runtime_state = &state,
-                                    .scanner_profile = nullptr,
-                            })
-                        .ok());
-
-    ASSERT_TRUE(reader.prepare_split(build_split_options(local_file)).ok());
-    Block block = build_table_block(projected_columns);
-    bool eos = false;
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    ASSERT_FALSE(eos);
-    expect_int32_column_values(*block.get_by_position(0).column, {3});
-    ASSERT_TRUE(reader.close().ok());
-
-    // The same predicate cannot be file-local when this split omits `id`. It 
must be rebuilt as a
-    // table-level predicate over the materialized NULL instead of inheriting 
the previous split's
-    // file-local ownership or escaping without exact evaluation.
-    ASSERT_TRUE(reader.prepare_split(build_split_options(missing_file)).ok());
-    block = build_table_block(projected_columns);
-    eos = false;
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    EXPECT_FALSE(eos);
-    EXPECT_EQ(block.rows(), 0);
-    ASSERT_TRUE(reader.get_block(&block, &eos).ok());
-    EXPECT_TRUE(eos);
-
-    ASSERT_TRUE(reader.close().ok());
-    std::filesystem::remove_all(test_dir);
-}
-
 TEST(TableReaderTest, CreateScanRequestDeduplicatesSharedPredicateColumns) {
     const auto int_type = std::make_shared<DataTypeInt32>();
     const std::vector<ColumnDefinition> projected_columns = {
diff --git a/be/test/storage/segment/adaptive_block_size_predictor_test.cpp 
b/be/test/storage/segment/adaptive_block_size_predictor_test.cpp
index 64795dace19..60b6f37b8ce 100644
--- a/be/test/storage/segment/adaptive_block_size_predictor_test.cpp
+++ b/be/test/storage/segment/adaptive_block_size_predictor_test.cpp
@@ -89,18 +89,6 @@ TEST_F(AdaptiveBlockSizePredictorTest, 
NoHistoryReturnsMaxRows) {
     EXPECT_DOUBLE_EQ(pred.bytes_per_row_for_test(), expected_bpr);
 }
 
-TEST_F(AdaptiveBlockSizePredictorTest, 
ExplicitMaterializedSampleUsesPreFilterShape) {
-    AdaptiveBlockSizePredictor pred(kBlockBytes, 0.0);
-
-    // Callers that filter a block before returning it can still report the 
rows and bytes that
-    // were actually materialized upstream.
-    pred.update(32, 32 * 4096);
-
-    EXPECT_TRUE(pred.has_history_for_test());
-    EXPECT_DOUBLE_EQ(pred.bytes_per_row_for_test(), 4096.0);
-    EXPECT_EQ(pred.predict_next_rows(), 2048);
-}
-
 // ── Test 2: EWMA convergence 
──────────────────────────────────────────────────
 // When every update delivers the same sample, the EWMA stays exactly at that
 // value (0.9*v + 0.1*v == v for any v).


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to