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 5c2991d958a branch-4.2: [improvement](be) Cut per-document work in 
SNII docid decode #69909 (#69955)
5c2991d958a is described below

commit 5c2991d958a60f942186933a87eb7bdc986c98d1
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Nov 3 09:57:15 2026 +0800

    branch-4.2: [improvement](be) Cut per-document work in SNII docid decode 
#69909 (#69955)
    
    Cherry-picked from #69909
    
    Co-authored-by: Jack <[email protected]>
---
 .../storage/index/inverted/snii/encoding/pfor.cpp  | 12 ++---
 .../storage/index/inverted/snii/format/frq_pod.cpp | 37 ++++++++++-----
 .../inverted/snii/query/docid_posting_reader.cpp   |  2 +-
 .../index/inverted/snii/format/frq_pod_test.cpp    | 55 ++++++++++++++++++++++
 4 files changed, 86 insertions(+), 20 deletions(-)

diff --git a/be/src/storage/index/inverted/snii/encoding/pfor.cpp 
b/be/src/storage/index/inverted/snii/encoding/pfor.cpp
index f8ad4f15959..2a05fdcddbf 100644
--- a/be/src/storage/index/inverted/snii/encoding/pfor.cpp
+++ b/be/src/storage/index/inverted/snii/encoding/pfor.cpp
@@ -417,15 +417,15 @@ Status pfor_decode(ByteSource* src, size_t n, uint32_t* 
out) {
                 "pfor bit width {} exceeds 32", w);
     }
     uint32_t n_exc;
-    RETURN_IF_ERROR(src->get_varint32(&n_exc));
+    RETURN_IF_ERROR(src->get_varint32_fast(&n_exc));
     RETURN_IF_ERROR(bitunpack(src, n, w, out));
     // 64 bits wide: a delta from the file is at most 2^32 - 1 and idx is 
below n before every add, so the
     // sum cannot wrap back into [0, n) the way a 32-bit one can.
     uint64_t idx = 0;
     for (uint32_t i = 0; i < n_exc; ++i) {
         uint32_t d, val;
-        RETURN_IF_ERROR(src->get_varint32(&d));
-        RETURN_IF_ERROR(src->get_varint32(&val));
+        RETURN_IF_ERROR(src->get_varint32_fast(&d));
+        RETURN_IF_ERROR(src->get_varint32_fast(&val));
         idx += d;
         if (idx >= n) {
             return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, 
false>(
@@ -440,7 +440,7 @@ Status pfor_skip(ByteSource* src, size_t n) {
     uint8_t w = 0;
     RETURN_IF_ERROR(src->get_u8(&w));
     uint32_t n_exc = 0;
-    RETURN_IF_ERROR(src->get_varint32(&n_exc));
+    RETURN_IF_ERROR(src->get_varint32_fast(&n_exc));
     const size_t packed = (static_cast<size_t>(w) * n + 7) / 8;
     Slice unused;
     RETURN_IF_ERROR(src->get_bytes(packed, &unused));
@@ -448,8 +448,8 @@ Status pfor_skip(ByteSource* src, size_t n) {
     for (uint32_t i = 0; i < n_exc; ++i) {
         uint32_t d = 0;
         uint32_t val = 0;
-        RETURN_IF_ERROR(src->get_varint32(&d));
-        RETURN_IF_ERROR(src->get_varint32(&val));
+        RETURN_IF_ERROR(src->get_varint32_fast(&d));
+        RETURN_IF_ERROR(src->get_varint32_fast(&val));
         idx += d;
         if (idx >= n) {
             return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, 
false>(
diff --git a/be/src/storage/index/inverted/snii/format/frq_pod.cpp 
b/be/src/storage/index/inverted/snii/format/frq_pod.cpp
index 2685cf01c86..3be6087f259 100644
--- a/be/src/storage/index/inverted/snii/format/frq_pod.cpp
+++ b/be/src/storage/index/inverted/snii/format/frq_pod.cpp
@@ -358,23 +358,34 @@ Status decode_dd_region_impl(Slice dd_disk, const 
FrqRegionMeta& meta, uint64_t
         return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, false>(
                 "frq: trailing bytes after dd region payload");
     }
-    uint64_t cur = win_base;
-    if (n != 0 && cur > std::numeric_limits<uint32_t>::max()) {
+    if (n == 0) {
+        return Status::OK();
+    }
+    if (win_base > std::numeric_limits<uint32_t>::max()) {
         return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, false>(
                 "frq: window base exceeds uint32 docid range");
     }
+    // A window is checked whole, so the sum below takes no branch per 
document: only the first
+    // delta may be zero, and the last docid must fit 32 bits.
+    uint32_t* const docs = docids->data();
+    uint64_t last = win_base + docs[0];
+    uint32_t smallest_delta = std::numeric_limits<uint32_t>::max();
+    for (uint32_t i = 1; i < n; ++i) {
+        last += docs[i];
+        smallest_delta = std::min(smallest_delta, docs[i]);
+    }
+    if (smallest_delta == 0) {
+        return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, false>(
+                "frq: zero docid delta");
+    }
+    if (last > std::numeric_limits<uint32_t>::max()) {
+        return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, false>(
+                "frq: docid accumulation overflow");
+    }
+    auto docid = static_cast<uint32_t>(win_base);
     for (uint32_t i = 0; i < n; ++i) {
-        const uint32_t delta = (*docids)[i];
-        if (i != 0 && delta == 0) {
-            return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, 
false>(
-                    "frq: zero docid delta");
-        }
-        if (delta > std::numeric_limits<uint32_t>::max() - cur) {
-            return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, 
false>(
-                    "frq: docid accumulation overflow");
-        }
-        cur += delta;
-        (*docids)[i] = static_cast<uint32_t>(cur);
+        docid += docs[i];
+        docs[i] = docid;
     }
     return Status::OK();
 }
diff --git a/be/src/storage/index/inverted/snii/query/docid_posting_reader.cpp 
b/be/src/storage/index/inverted/snii/query/docid_posting_reader.cpp
index 5193d2aa712..3add9c2d607 100644
--- a/be/src/storage/index/inverted/snii/query/docid_posting_reader.cpp
+++ b/be/src/storage/index/inverted/snii/query/docid_posting_reader.cpp
@@ -172,7 +172,7 @@ Status decode_window_prefix_plan(const 
io::BatchRangeFetcher& fetcher, const Win
             RETURN_IF_ERROR(sink->append_range(first, 
static_cast<uint64_t>(meta.last_docid) + 1));
             continue;
         }
-        docs.clear();
+        // docs is decoded over, not cleared: a resize zero-fills only what it 
adds.
         positions.clear();
         RETURN_IF_ERROR(reader::decode_window_slices(meta, dd_region, Slice(),
                                                      /*want_positions=*/false, 
&docs, &positions));
diff --git a/be/test/storage/index/inverted/snii/format/frq_pod_test.cpp 
b/be/test/storage/index/inverted/snii/format/frq_pod_test.cpp
index 961abad1958..1626c99a671 100644
--- a/be/test/storage/index/inverted/snii/format/frq_pod_test.cpp
+++ b/be/test/storage/index/inverted/snii/format/frq_pod_test.cpp
@@ -22,6 +22,7 @@
 #include <algorithm>
 #include <cstdint>
 #include <limits>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -207,6 +208,60 @@ TEST(SniiFrqPod, 
DecodeRejectsZeroInteriorDeltaAndDocidOverflow) {
     
EXPECT_TRUE(overflow.is<doris::ErrorCode::INVERTED_INDEX_FILE_CORRUPTED>()) << 
overflow;
 }
 
+// A window is checked whole before its deltas are summed. Only the first 
delta may be zero, a zero
+// anywhere else in any PFOR run is found, and the sum may reach the last 
docid but not pass it.
+TEST(SniiFrqPod, DecodeChecksEveryDeltaOfTheWindow) {
+    constexpr uint32_t kLastDocid = std::numeric_limits<uint32_t>::max();
+    constexpr size_t kRun = doris::snii::format::kFrqBaseUnit;
+    U32Vec docids;
+    auto decode = [&docids](const U32Vec& deltas, uint64_t win_base) {
+        auto [bytes, meta] = MakeRawDdFromDeltas(deltas);
+        return decode_dd_region(Slice(bytes), meta, win_base, &docids);
+    };
+    auto expect_corrupt = [](const Status& status, const std::string& message) 
{
+        
EXPECT_TRUE(status.is<doris::ErrorCode::INVERTED_INDEX_FILE_CORRUPTED>()) << 
status;
+        EXPECT_NE(status.to_string().find(message), std::string::npos) << 
status;
+    };
+
+    U32Vec deltas(3 * kRun, 3);
+    deltas[0] = 0;
+    ASSERT_TRUE(decode(deltas, /*win_base=*/7).ok());
+    EXPECT_EQ(docids.front(), 7U);
+    EXPECT_EQ(docids[1], 10U);
+    EXPECT_EQ(docids.back(), 7U + 3U * (3 * kRun - 1));
+    for (const size_t at : {size_t {1}, kRun - 1, kRun, 3 * kRun - 1}) {
+        U32Vec with_zero = deltas;
+        with_zero[at] = 0;
+        expect_corrupt(decode(with_zero, 7), "zero docid delta");
+    }
+
+    ASSERT_TRUE(decode({1, kLastDocid - 2}, /*win_base=*/1).ok());
+    EXPECT_EQ(docids, (U32Vec {2, kLastDocid}));
+    ASSERT_TRUE(decode({0}, kLastDocid).ok());
+    EXPECT_EQ(docids, (U32Vec {kLastDocid}));
+    expect_corrupt(decode({1, kLastDocid - 1}, 1), "docid accumulation 
overflow");
+    // These add up to 2^32, which is 0 in 32 bits.
+    expect_corrupt(decode(U32Vec(16, 1U << 28), 0), "docid accumulation 
overflow");
+    expect_corrupt(decode({0}, static_cast<uint64_t>(kLastDocid) + 1),
+                   "window base exceeds uint32 docid range");
+}
+
+// The docid reader decodes each window over the vector of the one before.
+TEST(SniiFrqPod, DecodeOverAVectorOfAnotherSize) {
+    U32Vec large(3000);
+    for (uint32_t i = 0; i < large.size(); ++i) {
+        large[i] = i * 5;
+    }
+    const U32Vec small = {5, 9, 12};
+    U32Vec out;
+    ASSERT_TRUE(RoundTrip(large, 0, 0, &out).ok());
+    EXPECT_EQ(out, large);
+    ASSERT_TRUE(RoundTrip(small, 0, 0, &out).ok());
+    EXPECT_EQ(out, small);
+    ASSERT_TRUE(RoundTrip(large, 0, 0, &out).ok());
+    EXPECT_EQ(out, large);
+}
+
 // first_docid < win_base (dd[0] would underflow) -> InvalidArgument.
 TEST(SniiFrqPod, FirstDocBelowWinBaseRejected) {
     U32Vec docs = {100, 200};


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

Reply via email to