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

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new c3259a554e1 branch-4.2: [improvement](be) Add up SNII position counts 
a run at a time in selective decode #69891 (#69956)
c3259a554e1 is described below

commit c3259a554e1cb3585036e2e697fcb4f1a930e7f4
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Nov 3 10:01:50 2026 +0800

    branch-4.2: [improvement](be) Add up SNII position counts a run at a time 
in selective decode #69891 (#69956)
    
    Cherry-picked from #69891
    
    Co-authored-by: Jack <[email protected]>
---
 .../storage/index/inverted/snii/format/prx_pod.cpp | 47 +++++++++-----
 .../index/inverted/snii/format/prx_pod_test.cpp    | 75 ++++++++++++++++++++++
 2 files changed, 105 insertions(+), 17 deletions(-)

diff --git a/be/src/storage/index/inverted/snii/format/prx_pod.cpp 
b/be/src/storage/index/inverted/snii/format/prx_pod.cpp
index d6ef8638802..62ca0274910 100644
--- a/be/src/storage/index/inverted/snii/format/prx_pod.cpp
+++ b/be/src/storage/index/inverted/snii/format/prx_pod.cpp
@@ -23,6 +23,7 @@
 #include <chrono>
 #include <cstddef>
 #include <limits>
+#include <numeric>
 #include <span>
 #include <vector>
 
@@ -819,6 +820,9 @@ bool should_decode_full_prx_positions(std::span<const 
SelectedRange> selected,
     return covered_runs * 4 >= total_runs * 3;
 }
 
+// The count of every document of the window is decoded to find where the 
selected documents' deltas
+// start, and a sparse candidate set selects none in most runs. So a run is 
added up whole, with no
+// branch per document, and then walked from one selected document to the next.
 Status decode_selected_pfor_count_ranges(ByteSource* src, uint32_t doc_count,
                                          std::span<const uint32_t> 
doc_ordinals,
                                          std::vector<SelectedRange>& selected,
@@ -832,7 +836,6 @@ Status decode_selected_pfor_count_ranges(ByteSource* src, 
uint32_t doc_count,
     pos_off.push_back(0);
 
     *selected_pos_count = 0;
-    uint32_t delta_begin = 0;
     size_t next_doc = 0;
     *total_pos_count = 0;
     *max_frequency = 0;
@@ -841,24 +844,34 @@ Status decode_selected_pfor_count_ranges(ByteSource* src, 
uint32_t doc_count,
     for (uint32_t run_begin = 0; run_begin < doc_count; run_begin += 
kFrqBaseUnit) {
         const uint32_t run_len = std::min<uint32_t>(kFrqBaseUnit, doc_count - 
run_begin);
         RETURN_IF_ERROR(pfor_decode(src, run_len, run_buf.data()));
+        uint64_t run_sum = 0;
+        uint32_t run_max = 0;
+        uint32_t run_min = std::numeric_limits<uint32_t>::max();
         for (uint32_t i = 0; i < run_len; ++i) {
-            const uint32_t d = run_begin + i;
-            const uint32_t count = run_buf[i];
-            *max_frequency = std::max(*max_frequency, count);
-            *has_zero_frequency |= count == 0;
-            *total_pos_count += count;
-            if (*total_pos_count > kMaxWindowPositions) {
-                return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, 
false>(
-                        "prx: pos_count sum exceeds sane cap");
-            }
-            if (next_doc < doc_ordinals.size() && doc_ordinals[next_doc] == d) 
{
-                selected.emplace_back(delta_begin, delta_begin + count, 
*selected_pos_count);
-                *selected_pos_count += count;
-                pos_off.push_back(*selected_pos_count);
-                ++next_doc;
-            }
-            delta_begin += count;
+            run_sum += run_buf[i];
+            run_max = std::max(run_max, run_buf[i]);
+            run_min = std::min(run_min, run_buf[i]);
+        }
+        *max_frequency = std::max(*max_frequency, run_max);
+        *has_zero_frequency |= run_min == 0;
+        if (*total_pos_count + run_sum > kMaxWindowPositions) {
+            return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, 
false>(
+                    "prx: pos_count sum exceeds sane cap");
+        }
+        // Under the cap, so every sum below fits 32 bits.
+        auto delta_begin = static_cast<uint32_t>(*total_pos_count);
+        const uint32_t* walked = run_buf.data();
+        for (; next_doc < doc_ordinals.size() && doc_ordinals[next_doc] - 
run_begin < run_len;
+             ++next_doc) {
+            const uint32_t* doc = run_buf.data() + (doc_ordinals[next_doc] - 
run_begin);
+            delta_begin = std::accumulate(walked, doc, delta_begin);
+            selected.emplace_back(delta_begin, delta_begin + *doc, 
*selected_pos_count);
+            *selected_pos_count += *doc;
+            pos_off.push_back(*selected_pos_count);
+            delta_begin += *doc;
+            walked = doc + 1;
         }
+        *total_pos_count += run_sum;
     }
     if (next_doc != doc_ordinals.size()) {
         return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, false>(
diff --git a/be/test/storage/index/inverted/snii/format/prx_pod_test.cpp 
b/be/test/storage/index/inverted/snii/format/prx_pod_test.cpp
index 8ff976e2a2c..695ffa3bddc 100644
--- a/be/test/storage/index/inverted/snii/format/prx_pod_test.cpp
+++ b/be/test/storage/index/inverted/snii/format/prx_pod_test.cpp
@@ -957,6 +957,81 @@ TEST(SniiPrxPod, SelectiveCsrRejectsInvalidOrdinals) {
     }
 }
 
+// The counts of a PFOR run are added up whole, then walked from one selected 
document to the next.
+// Sparse and dense selections, the first and last documents of runs, and 
documents with no positions
+// or with enough to be PFOR exceptions all come out as the input, with the 
shape of the whole window.
+TEST(SniiPrxPod, SelectivePforMatchesInputForRandomWindows) {
+    std::mt19937 rng(20261030);
+    for (int round = 0; round < 300; ++round) {
+        const uint32_t doc_count = 1 + rng() % (3 * kFrqBaseUnit + 20);
+        const uint32_t style = rng() % 3;
+        PerDoc docs(doc_count);
+        PrxDecodedShape want_shape {.total_docs = doc_count};
+        for (auto& doc : docs) {
+            uint32_t count = 1 + rng() % 3;
+            if (style >= 1 && rng() % 5 == 0) {
+                count = 0;
+            } else if (style == 2 && rng() % 50 == 0) {
+                count = 300 + rng() % 900;
+            }
+            uint32_t position = rng() % 50;
+            for (uint32_t i = 0; i < count; ++i) {
+                doc.push_back(position);
+                position += 1 + rng() % 40;
+            }
+            want_shape.total_positions += count;
+            want_shape.max_frequency = std::max(want_shape.max_frequency, 
count);
+            want_shape.has_zero_frequency |= count == 0;
+        }
+        std::vector<uint32_t> ordinals;
+        const uint32_t mode = rng() % 4;
+        for (uint32_t d = 0; d < doc_count; ++d) {
+            const bool run_edge = d % kFrqBaseUnit == 0 || d % kFrqBaseUnit == 
kFrqBaseUnit - 1;
+            const bool take = mode == 0   ? (rng() % 40 == 0)
+                              : mode == 1 ? (rng() % 2 == 0)
+                              : mode == 2 ? run_edge
+                                          : (d == 0 || d + 1 == doc_count);
+            if (take) {
+                ordinals.push_back(d);
+            }
+        }
+        SCOPED_TRACE("round=" + std::to_string(round) + " docs=" + 
std::to_string(doc_count) +
+                     " style=" + std::to_string(style) + " mode=" + 
std::to_string(mode));
+        const std::vector<uint8_t> encoded = BuildProfileFrame(docs, 
PrxCodec::kPfor);
+        std::vector<uint32_t> expected_flat;
+        std::vector<uint32_t> expected_offsets;
+        ExpectedSelectiveCsr(docs, ordinals, &expected_flat, 
&expected_offsets);
+        std::vector<uint32_t> flat;
+        std::vector<uint32_t> offsets;
+        PrxDecodedShape shape;
+        PrxDecodeContext context {.shape = &shape};
+        ByteSource source((Slice(encoded)));
+        ASSERT_TRUE(
+                read_prx_window_csr_selective(&source, ordinals, &flat, 
&offsets, &context).ok());
+        EXPECT_EQ(flat, expected_flat);
+        EXPECT_EQ(offsets, expected_offsets);
+        EXPECT_EQ(shape.total_docs, want_shape.total_docs);
+        EXPECT_EQ(shape.total_positions, want_shape.total_positions);
+        EXPECT_EQ(shape.max_frequency, want_shape.max_frequency);
+        EXPECT_EQ(shape.has_zero_frequency, want_shape.has_zero_frequency);
+    }
+}
+
+// A run is checked against the cap as a whole, and its sum is kept in 64 
bits: these counts add up
+// to 2^38, which is 0 in 32 bits and would match the declared total.
+TEST(SniiPrxPod, SelectivePforRejectsCountsAboveTheCap) {
+    const std::vector<uint32_t> freqs(kFrqBaseUnit, 1U << 30);
+    const std::vector<uint8_t> encoded = MakePforWindow(kFrqBaseUnit, 0, 
freqs, {});
+    const std::vector<uint32_t> ordinals = {3};
+    std::vector<uint32_t> flat;
+    std::vector<uint32_t> offsets;
+    ByteSource source((Slice(encoded)));
+    const Status status = read_prx_window_csr_selective(&source, ordinals, 
&flat, &offsets);
+    EXPECT_TRUE(status.is<doris::ErrorCode::INVERTED_INDEX_FILE_CORRUPTED>()) 
<< status;
+    EXPECT_NE(status.to_string().find("pos_count sum exceeds sane cap"), 
std::string::npos)
+            << status;
+}
+
 // Multiple docs, positions ascending within each doc, no shared baseline 
across
 // docs.
 TEST(SniiPrxPod, MultiDocRoundTrip) {


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

Reply via email to