Coverage Report

Created: 2026-11-07 01:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/json/path_in_data.h
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
// This file is copied from
18
// https://github.com/ClickHouse/ClickHouse/blob/master/src/DataTypes/Serializations/PathInData.h
19
// and modified by Doris
20
21
#pragma once
22
23
#include <gen_cpp/segment_v2.pb.h>
24
#include <stddef.h>
25
26
#include <algorithm>
27
#include <memory>
28
#include <string>
29
#include <string_view>
30
#include <vector>
31
32
#include "core/uint128.h"
33
34
namespace doris {
35
36
class Status;
37
std::string encode_variant_path_component(std::string_view key);
38
Status decode_variant_path_component(std::string_view key, std::string* decoded);
39
class PathInData;
40
PathInData make_variant_storage_path(const std::string& root,
41
                                     const std::vector<std::string>& components, bool escape_dots);
42
43
/// Class that represents path in document, e.g. JSON.
44
using PathInDataPtr = std::shared_ptr<PathInData>;
45
46
class PathInData {
47
public:
48
    struct Part {
49
35.5k
        Part() = default;
50
        Part(std::string_view key_, bool is_nested_, UInt8 anonymous_array_level_)
51
254k
                : key(key_), is_nested(is_nested_), anonymous_array_level(anonymous_array_level_) {}
52
23.3k
        bool operator==(const Part& other) const {
53
23.3k
            return this->key == other.key && this->is_nested == other.is_nested &&
54
23.3k
                   this->anonymous_array_level == other.anonymous_array_level;
55
23.3k
        }
56
        /// Name of part of path.
57
        std::string_view key;
58
        /// If this part is Nested, i.e. element
59
        /// related to this key is the array of objects.
60
        bool is_nested = false;
61
        /// Number of array levels between current key and previous key.
62
        /// E.g. in JSON {"k1": [[[{"k2": 1, "k3": 2}]]]}
63
        /// "k1" is nested and has anonymous_array_level = 0.
64
        /// "k2" and "k3" are not nested and have anonymous_array_level = 2.
65
        UInt8 anonymous_array_level = 0;
66
67
        /// Get the total array depth for this part.
68
        /// Used for NestedGroup offset indexing.
69
        /// If is_nested (array<object>), the depth is anonymous_array_level + 1
70
        /// Otherwise it's just the anonymous_array_level.
71
0
        UInt8 get_array_depth() const {
72
0
            return is_nested ? (anonymous_array_level + 1) : anonymous_array_level;
73
0
        }
74
    };
75
    using Parts = std::vector<Part>;
76
74.1k
    PathInData() = default;
77
    explicit PathInData(std::string_view path_, bool is_typed_ = false);
78
    explicit PathInData(const Parts& parts_);
79
    explicit PathInData(std::string_view path_, const Parts& parts_, bool is_typed_ = false);
80
    explicit PathInData(const std::vector<std::string>& paths);
81
    explicit PathInData(const std::string& root, const std::vector<std::string>& paths);
82
    PathInData(const PathInData& other);
83
    PathInData& operator=(const PathInData& other);
84
    static UInt128 get_parts_hash(const Parts& parts_, bool is_typed_ = false);
85
380k
    bool empty() const { return parts.empty(); }
86
327k
    const String& get_path() const { return path; }
87
    // if path is v.a.b, then relative path will return a.b
88
    // make sure the parts is not empty
89
0
    std::string_view get_relative_path() const {
90
0
        if (parts.size() <= 1) {
91
0
            // return empty string
92
0
            return {};
93
0
        }
94
0
        return {path.begin() + parts[0].key.size() + 1, path.end()};
95
0
    }
96
463k
    const Parts& get_parts() const { return parts; }
97
0
    bool is_nested(size_t i) const { return parts[i].is_nested; }
98
6.41k
    bool has_nested_part() const { return has_nested; }
99
29.6k
    bool operator==(const PathInData& other) const {
100
29.6k
        return parts == other.parts && is_typed == other.is_typed;
101
29.6k
    }
102
1
    bool operator!=(const PathInData& other) const { return !(*this == other); }
103
    PathInData get_nested_prefix_path() const;
104
    struct Hash {
105
        size_t operator()(const PathInData& value) const;
106
    };
107
    std::string to_jsonpath() const;
108
    PathInData copy_pop_front() const;
109
    PathInData copy_pop_nfront(size_t n) const;
110
    PathInData copy_pop_back() const;
111
    static bool try_strip_prefix(const std::string& name, const std::string& prefix_dot,
112
                                 std::string* out);
113
    static PathInData append(const PathInData& base, std::string_view suffix);
114
    void to_protobuf(segment_v2::ColumnPathInfo* pb, int32_t parent_col_unique_id) const;
115
    void from_protobuf(const segment_v2::ColumnPathInfo& pb);
116
117
3.97k
    bool get_is_typed() const { return is_typed; }
118
119
    bool need_record_stats() const;
120
121
30.7k
    bool operator<(const PathInData& rhs) const {
122
30.7k
        return std::lexicographical_compare(
123
30.7k
                parts.begin(), parts.end(), rhs.parts.begin(), rhs.parts.end(),
124
43.9k
                [](const auto& a, const auto& b) { return a.key < b.key; });
125
30.7k
    }
126
127
private:
128
    /// Creates full path from parts.
129
    void build_path(const Parts& other_parts);
130
    /// Creates new parts full from full path with correct string pointers.
131
    void build_parts(const Parts& other_parts);
132
    /// The full path. Parts are separated by dots.
133
    String path;
134
    /// Parts of the path. All string_view-s in parts must point to the @path.
135
    Parts parts;
136
    /// True if at least one part is nested.
137
    /// Cached to avoid linear complexity at 'has_nested'.
138
    bool has_nested = false;
139
140
    /// True if the path is typed, e.g. a.b: int
141
    bool is_typed = false;
142
};
143
144
// Heap bytes retained beyond sizeof(PathInData): the dotted-path string buffer
145
// plus the parts vector.
146
21.6k
inline size_t path_allocated_bytes(const PathInData& path) {
147
21.6k
    return path.get_path().capacity() + path.get_parts().capacity() * sizeof(PathInData::Part);
148
21.6k
}
149
150
class PathInDataBuilder {
151
public:
152
56
    const PathInData::Parts& get_parts() const { return parts; }
153
    PathInDataBuilder& append(std::string_view key, bool is_array);
154
    PathInDataBuilder& append(const PathInData::Parts& path, bool is_array);
155
    PathInDataBuilder& append(const std::vector<std::string>& parts);
156
    void pop_back();
157
    void pop_back(size_t n);
158
27.5k
    PathInData build() { return PathInData(parts); }
159
160
private:
161
    PathInData::Parts parts;
162
    /// Number of array levels without key to which
163
    /// next non-empty key will be nested.
164
    /// Example: for JSON { "k1": [[{"k2": 1, "k3": 2}] }
165
    // `k2` and `k3` has anonymous_array_level = 1 in that case.
166
    size_t current_anonymous_array_level = 0;
167
};
168
using PathsInData = std::vector<PathInData>;
169
170
struct PathInDataRef {
171
    const PathInData* ref;
172
    struct Hash {
173
711
        size_t operator()(const PathInDataRef& value) const {
174
711
            return PathInData::Hash {}(*value.ref);
175
711
        }
176
    };
177
711
    PathInDataRef(const PathInData* ptr) : ref(ptr) {}
178
0
    PathInDataRef() : ref(nullptr) {}
179
9
    bool operator==(const PathInDataRef& other) const {
180
9
        return (this->ref != nullptr && other.ref != nullptr && *this->ref == *other.ref) ||
181
9
               (this->ref == nullptr && other.ref == nullptr);
182
9
    }
183
};
184
185
} // namespace doris
186
187
template <>
188
struct std::hash<doris::PathInData> {
189
31.5k
    size_t operator()(const doris::PathInData& value) const {
190
31.5k
        return doris::PathInData::Hash {}(value);
191
31.5k
    }
192
};