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


##########
be/src/format_v2/parquet/selection_vector.h:
##########
@@ -173,19 +197,65 @@ class SelectionVector {
             }
             previous = current;
         }
+        if (!_mutable_data_exposed) {
+            _verified_generation = _generation;
+            _verified_count = count;
+            _verified_batch_rows = batch_rows;
+        }
         return Status::OK();
     }
 
 private:
+    void _materialize_identity() {
+        if (_data != nullptr) {
+            return;
+        }
+        _owned.resize(_size);
+        _data = _owned.data();
+        for (size_t idx = 0; idx < _size; ++idx) {
+            _data[idx] = static_cast<Index>(idx);
+        }
+    }
+
+    size_t _compact(const uint8_t* filter, size_t count, bool 
filter_uses_row_index) {
+        DORIS_CHECK(filter != nullptr);
+        DORIS_CHECK(count <= _size);
+        Index* source = _data;
+        if (_data == nullptr) {
+            _owned.resize(_size);

Review Comment:
   [P2] Retain the owned selection scratch across batch resets. Every request 
with row/delete conjuncts calls `resize(batch_rows)`, which now clears 
`_owned`; the first rejecting predicate then executes `_owned.resize(_size)` 
and value-initializes the full index buffer before overwriting survivors. This 
is the repeated cost behind the PR's own roughly 30-61% 
dense/successive-compaction regressions, and production pays it for every 
filtered batch. Please keep identity implicit via `_data == nullptr` while 
retaining a high-water owned buffer (growing it lazily only when needed), then 
rerun the named selection cases and a predicate-reader case.



##########
be/test/format_v2/parquet/parquet_scan_test.cpp:
##########
@@ -3163,17 +3179,82 @@ TEST_F(ParquetScanTest, 
PredicateOnlyDictionaryRangeSkipsTypedValueMaterializati
     conjunct->close();
 }
 
+TEST_F(ParquetScanTest, DictionaryFiltersAreBuiltFromEachReaderSnapshot) {
+    struct ScanResult {
+        std::vector<int32_t> scores;
+        int64_t typed_compare_columns = 0;
+    };
+
+    auto scan = [&](int32_t lower_bound) {
+        RuntimeProfile profile("profile");
+        RuntimeState state {TQueryOptions(), TQueryGlobals()};
+        auto reader = create_reader(0, -1, &profile);
+        EXPECT_TRUE(reader->init(&state).ok());
+
+        std::vector<format::ColumnDefinition> schema;
+        EXPECT_TRUE(reader->get_schema(&schema).ok());
+        auto request = std::make_shared<format::FileScanRequest>();
+        format::FileScanRequestBuilder request_builder(request.get());
+        
EXPECT_TRUE(request_builder.add_predicate_column(format::LocalColumnId(0)).ok());
+        
EXPECT_TRUE(request_builder.add_non_predicate_column(format::LocalColumnId(1)).ok());
+        request->predicate_only_columns.push_back(format::LocalColumnId(0));
+        auto conjunct =
+                create_int32_function_conjunct(0, "gt", TExprOpcode::GT, 
lower_bound, false);
+        EXPECT_TRUE(conjunct->prepare(&state, RowDescriptor()).ok());
+        EXPECT_TRUE(conjunct->open(&state).ok());
+        request->conjuncts.push_back(conjunct);
+        EXPECT_TRUE(reader->open(request).ok());
+
+        ScanResult result;
+        bool eof = false;
+        while (!eof) {
+            Block block = build_file_block(schema);
+            size_t rows = 0;
+            EXPECT_TRUE(reader->get_block(&block, &rows, &eof).ok());
+            const auto& score_column = 
int32_data_column(*block.get_by_position(1).column);
+            for (size_t row = 0; row < rows; ++row) {
+                result.scores.push_back(score_column.get_element(row));
+            }
+        }
+        result.typed_compare_columns = counter_value(profile, 
"DictFilterTypedCompareColumns");
+        conjunct->close();
+        EXPECT_TRUE(reader->close().ok());
+        return result;
+    };
+
+    write_dictionary_int_pair_parquet_file(_file_path);
+    const auto first = scan(2);
+    EXPECT_EQ(first.scores, std::vector<int32_t>({30, 40, 50, 60}));
+    EXPECT_EQ(first.typed_compare_columns, 1);
+
+    const auto repeated = scan(2);
+    EXPECT_EQ(repeated.scores, first.scores);
+    EXPECT_EQ(repeated.typed_compare_columns, 1);
+
+    const auto changed_predicate = scan(3);
+    EXPECT_EQ(changed_predicate.scores, std::vector<int32_t>({40, 50, 60}));
+    EXPECT_EQ(changed_predicate.typed_compare_columns, 1);
+
+    write_dictionary_int_pair_parquet_file(_file_path, {1, 2, 7, 8, 9, 10});

Review Comment:
   [P2] Make this replacement dictionary change the matching entry-ID bitmap. 
Both `[1,2,3,4,5,6]` and `[1,2,7,8,9,10]` under `id > 2` keep IDs 2-5 and 
return the same score positions, so an implementation that reuses the first 
dictionary's filter bitmap still passes the branch intended to catch that bug. 
Reorder values across the threshold (for example `[7,1,8,2,9,3]`) and assert 
the resulting different score positions.



##########
be/benchmark/parquet/AGENTS.md:
##########
@@ -46,7 +48,10 @@ be/output/lib/benchmark_test --benchmark_list_tests \
   | grep -c '^ParquetKernel/'   # currently 80
 
 be/output/lib/benchmark_test --benchmark_list_tests \
-  | grep -c '^ParquetReader/'   # currently 152
+  | grep -c '^ParquetSelection/' # currently 25
+
+be/output/lib/benchmark_test --benchmark_list_tests \
+  | grep -c '^ParquetReader/'   # currently 167

Review Comment:
   [P3] Please update the later `Current validation record` too. This section 
and the invariant tests now require 228 decoder, 80 kernel, 25 selection, and 
167 reader cases, but line 319 still omits Selection and says 152 Reader cases. 
Leaving two authoritative counts in this mandatory guide makes a correct smoke 
listing look invalid.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to