Coverage Report

Created: 2026-10-22 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/be/src/util/jsonb_document.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "util/jsonb_document.h"
19
20
#include <memory>
21
#include <string>
22
#include <vector>
23
24
#include "common/status.h"
25
#include "util/jsonb_writer.h"
26
27
namespace doris {
28
29
Status JsonbDocument::checkAndCreateDocument(const char* pb, size_t size,
30
48.4k
                                             const JsonbDocument** doc) {
31
48.4k
    *doc = nullptr;
32
48.4k
    if (!pb || size == 0) {
33
4
        static const std::string buf = []() {
34
1
            JsonbWriter writer;
35
1
            (void)writer.writeNull();
36
1
            auto* out = writer.getOutput();
37
1
            return std::string(out->getBuffer(), out->getSize());
38
1
        }();
39
        // Treat empty input as a valid JSONB null document.
40
4
        *doc = reinterpret_cast<const JsonbDocument*>(buf.data());
41
4
        return Status::OK();
42
4
    }
43
48.4k
    if (!pb || size < sizeof(JsonbHeader) + sizeof(JsonbValue)) {
44
0
        return Status::InvalidArgument("Invalid JSONB document: too small size({}) or null pointer",
45
0
                                       size);
46
0
    }
47
48
48.4k
    const auto* doc_ptr = (const JsonbDocument*)pb;
49
48.4k
    if (doc_ptr->header_.ver_ != JSONB_VER) {
50
4
        return Status::InvalidArgument("Invalid JSONB document: invalid version({})",
51
4
                                       doc_ptr->header_.ver_);
52
4
    }
53
54
48.4k
    const auto* val = (const JsonbValue*)doc_ptr->payload_;
55
48.4k
    if (val->type < JsonbType::T_Null || val->type >= JsonbType::NUM_TYPES ||
56
48.4k
        size != sizeof(JsonbHeader) + val->numPackedBytes()) {
57
0
        return Status::InvalidArgument("Invalid JSONB document: invalid type({}) or size({})",
58
0
                                       static_cast<JsonbTypeUnder>(val->type), size);
59
0
    }
60
61
48.4k
    *doc = doc_ptr;
62
48.4k
    return Status::OK();
63
48.4k
}
64
65
162
JsonbFindResult JsonbValue::findValue(JsonbPath& path) const {
66
162
    JsonbFindResult result;
67
162
    bool is_wildcard = false;
68
69
162
    std::vector<const JsonbValue*> values;
70
162
    std::vector<const JsonbValue*> results;
71
162
    results.emplace_back(this);
72
73
162
    if (path.is_supper_wildcard()) {
74
0
        std::function<void(const JsonbValue*)> foreach_values;
75
0
        foreach_values = [&](const JsonbValue* val) {
76
0
            if (val->isObject()) {
77
0
                for (const auto& it : *val->unpack<ObjectVal>()) {
78
0
                    results.emplace_back(it.value());
79
0
                    foreach_values(it.value());
80
0
                }
81
0
            } else if (val->isArray()) {
82
0
                for (const auto& it : *val->unpack<ArrayVal>()) {
83
0
                    results.emplace_back(&it);
84
0
                    foreach_values(&it);
85
0
                }
86
0
            }
87
0
        };
88
0
        is_wildcard = true;
89
0
        foreach_values(this);
90
0
    }
91
92
332
    for (size_t i = 0; i < path.get_leg_vector_size(); ++i) {
93
170
        values = std::move(results);
94
170
        for (const auto* pval : values) {
95
169
            switch (path.get_leg_from_leg_vector(i)->type) {
96
102
            case MEMBER_CODE: {
97
102
                if (LIKELY(pval->type == JsonbType::T_Object)) {
98
66
                    if (path.get_leg_from_leg_vector(i)->is_wildcard) {
99
2
                        is_wildcard = true;
100
5
                        for (const auto& it : *pval->unpack<ObjectVal>()) {
101
5
                            results.emplace_back(it.value());
102
5
                        }
103
2
                        continue;
104
2
                    }
105
106
64
                    pval = pval->unpack<ObjectVal>()->find(
107
64
                            path.get_leg_from_leg_vector(i)->leg_ptr,
108
64
                            path.get_leg_from_leg_vector(i)->leg_len);
109
110
64
                    if (pval) {
111
50
                        results.emplace_back(pval);
112
50
                    }
113
64
                }
114
100
                continue;
115
102
            }
116
100
            case ARRAY_CODE: {
117
67
                if (path.get_leg_from_leg_vector(i)->is_wildcard) {
118
0
                    if (LIKELY(pval->type == JsonbType::T_Array)) {
119
0
                        is_wildcard = true;
120
0
                        for (const auto& it : *pval->unpack<ArrayVal>()) {
121
0
                            results.emplace_back(&it);
122
0
                        }
123
0
                    }
124
0
                    continue;
125
0
                }
126
127
67
                if (pval->type != JsonbType::T_Array &&
128
67
                    path.get_leg_from_leg_vector(i)->array_index == 0) {
129
                    // Same as mysql and postgres
130
22
                    results.emplace_back(pval);
131
22
                    continue;
132
22
                }
133
134
45
                if (pval->type != JsonbType::T_Array ||
135
45
                    path.get_leg_from_leg_vector(i)->leg_ptr != nullptr ||
136
45
                    path.get_leg_from_leg_vector(i)->leg_len != 0) {
137
11
                    continue;
138
11
                }
139
140
34
                if (path.get_leg_from_leg_vector(i)->array_index >= 0) {
141
34
                    pval = pval->unpack<ArrayVal>()->get(
142
34
                            path.get_leg_from_leg_vector(i)->array_index);
143
34
                } else {
144
0
                    pval = pval->unpack<ArrayVal>()->get(
145
0
                            pval->unpack<ArrayVal>()->numElem() +
146
0
                            path.get_leg_from_leg_vector(i)->array_index);
147
0
                }
148
149
34
                if (pval) {
150
23
                    results.emplace_back(pval);
151
23
                }
152
34
                continue;
153
45
            }
154
169
            }
155
169
        }
156
170
    }
157
158
162
    if (is_wildcard) {
159
2
        result.is_wildcard = true;
160
2
        if (results.empty()) {
161
0
            result.value = nullptr; // No values found
162
2
        } else {
163
            /// if supper wildcard, need distinct results
164
            /// because supper wildcard will traverse all nodes
165
            ///
166
            /// `select json_extract( '[1]', '$**[0]' );`
167
            /// +---------------------------------+
168
            /// | json_extract( '[1]', '$**[0]' ) |
169
            /// +---------------------------------+
170
            /// | [1,1]                           |
171
            /// +---------------------------------+
172
2
            if (results.size() > 1 && path.is_supper_wildcard()) [[unlikely]] {
173
0
                std::set<const JsonbValue*> distinct_results;
174
0
                for (const auto* pval : results) {
175
0
                    distinct_results.insert(pval);
176
0
                }
177
0
                results.assign(distinct_results.begin(), distinct_results.end());
178
0
            }
179
2
            result.writer = std::make_unique<JsonbWriter>();
180
2
            result.writer->writeStartArray();
181
5
            for (const auto* pval : results) {
182
5
                result.writer->writeValue(pval);
183
5
            }
184
2
            result.writer->writeEndArray();
185
186
2
            const JsonbDocument* doc = nullptr;
187
2
            THROW_IF_ERROR(JsonbDocument::checkAndCreateDocument(
188
2
                    result.writer->getOutput()->getBuffer(), result.writer->getOutput()->getSize(),
189
2
                    &doc));
190
2
            result.value = doc->getValue();
191
2
        }
192
160
    } else if (results.size() == 1) {
193
88
        result.value = results[0];
194
88
    }
195
196
162
    return result;
197
162
}
198
199
std::vector<std::pair<StringRef, const JsonbValue*>> ObjectVal::get_ordered_key_value_pairs()
200
0
        const {
201
0
    std::vector<std::pair<StringRef, const JsonbValue*>> kvs;
202
0
    const auto* obj_val = this;
203
0
    for (auto it = obj_val->begin(); it != obj_val->end(); ++it) {
204
0
        kvs.emplace_back(StringRef(it->getKeyStr(), it->klen()), it->value());
205
0
    }
206
    // sort by key
207
0
    std::sort(kvs.begin(), kvs.end(),
208
0
              [](const auto& left, const auto& right) { return left.first < right.first; });
209
    // unique by key
210
0
    kvs.erase(std::unique(kvs.begin(), kvs.end(),
211
0
                          [](const auto& left, const auto& right) {
212
0
                              return left.first == right.first;
213
0
                          }),
214
0
              kvs.end());
215
0
    return kvs;
216
0
}
217
218
} // namespace doris