Coverage Report

Created: 2026-07-15 03:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/variant/variant_field.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/variant/variant_field.h"
19
20
#include <algorithm>
21
#include <cstring>
22
#include <limits>
23
#include <utility>
24
#include <vector>
25
26
#include "common/exception.h"
27
#include "util/utf8_check.h"
28
#include "util/variant/variant_encoding.h"
29
30
namespace doris {
31
namespace {
32
33
constexpr size_t METADATA_SIZE_PREFIX = sizeof(uint32_t);
34
constexpr unsigned __int128 DECIMAL4_MAX = 999'999'999;
35
constexpr unsigned __int128 DECIMAL8_MAX = 999'999'999'999'999'999;
36
37
0
constexpr unsigned __int128 max_decimal38() {
38
0
    unsigned __int128 value = 1;
39
0
    for (uint8_t digit = 0; digit < 38; ++digit) {
40
0
        value *= 10;
41
0
    }
42
0
    return value - 1;
43
0
}
44
45
constexpr unsigned __int128 MAX_DECIMAL38 = max_decimal38();
46
47
131k
uint32_t read_u32(const char* data) noexcept {
48
131k
    uint32_t result = 0;
49
657k
    for (uint8_t byte = 0; byte < METADATA_SIZE_PREFIX; ++byte) {
50
525k
        result |= static_cast<uint32_t>(static_cast<uint8_t>(data[byte])) << (byte * 8);
51
525k
    }
52
131k
    return result;
53
131k
}
54
55
232
void write_u32(char* destination, uint32_t value) noexcept {
56
1.16k
    for (uint8_t byte = 0; byte < METADATA_SIZE_PREFIX; ++byte) {
57
928
        destination[byte] = static_cast<char>(value >> (byte * 8));
58
928
    }
59
232
}
60
61
1.61k
void require_non_null(StringRef bytes, const char* description) {
62
1.61k
    if (bytes.size != 0 && bytes.data == nullptr) {
63
3
        throw Exception(ErrorCode::INVALID_ARGUMENT,
64
3
                        "VariantField {} has a null pointer for {} bytes", description, bytes.size);
65
3
    }
66
1.61k
}
67
68
20
unsigned __int128 magnitude(__int128 value) {
69
20
    const auto unsigned_value = static_cast<unsigned __int128>(value);
70
20
    return value < 0 ? ~unsigned_value + 1 : unsigned_value;
71
20
}
72
73
392
void require_valid_utf8(StringRef value, const char* description) {
74
392
    if (value.size != 0 && !validate_utf8(value.data, value.size)) {
75
10
        throw Exception(ErrorCode::CORRUPTION, "VariantField {} is not valid UTF-8", description);
76
10
    }
77
392
}
78
79
20
void require_decimal_in_range(const VariantDecimal& decimal) {
80
20
    unsigned __int128 maximum = MAX_DECIMAL38;
81
20
    if (decimal.width == 4) {
82
2
        maximum = DECIMAL4_MAX;
83
18
    } else if (decimal.width == 8) {
84
4
        maximum = DECIMAL8_MAX;
85
4
    }
86
20
    if (magnitude(decimal.unscaled) > maximum) {
87
8
        throw Exception(ErrorCode::CORRUPTION,
88
8
                        "VariantField decimal unscaled value exceeds precision for width {}",
89
8
                        decimal.width);
90
8
    }
91
20
}
92
93
789
void require_valid_primitive(VariantValueRef value) {
94
789
    switch (value.primitive_id()) {
95
95
    case VariantPrimitiveId::NULL_VALUE:
96
160
    case VariantPrimitiveId::TRUE_VALUE:
97
160
    case VariantPrimitiveId::FALSE_VALUE:
98
160
        return;
99
467
    case VariantPrimitiveId::INT8:
100
471
    case VariantPrimitiveId::INT16:
101
474
    case VariantPrimitiveId::INT32:
102
485
    case VariantPrimitiveId::INT64:
103
485
        static_cast<void>(value.get_int());
104
485
        return;
105
26
    case VariantPrimitiveId::DOUBLE:
106
26
        static_cast<void>(value.get_double());
107
26
        return;
108
4
    case VariantPrimitiveId::DECIMAL4:
109
8
    case VariantPrimitiveId::DECIMAL8:
110
22
    case VariantPrimitiveId::DECIMAL16:
111
22
        require_decimal_in_range(value.get_decimal());
112
22
        return;
113
16
    case VariantPrimitiveId::DATE:
114
16
        static_cast<void>(value.get_date());
115
16
        return;
116
27
    case VariantPrimitiveId::TIMESTAMP_MICROS:
117
27
        static_cast<void>(value.get_timestamp_micros());
118
27
        return;
119
27
    case VariantPrimitiveId::TIMESTAMP_NTZ_MICROS:
120
27
        static_cast<void>(value.get_timestamp_ntz_micros());
121
27
        return;
122
0
    case VariantPrimitiveId::FLOAT:
123
0
        static_cast<void>(value.get_float());
124
0
        return;
125
2
    case VariantPrimitiveId::BINARY:
126
2
        static_cast<void>(value.get_binary());
127
2
        return;
128
18
    case VariantPrimitiveId::STRING:
129
18
        require_valid_utf8(value.get_string(), "string");
130
18
        return;
131
0
    case VariantPrimitiveId::TIME_NTZ_MICROS:
132
0
        static_cast<void>(value.get_time_ntz_micros());
133
0
        return;
134
2
    case VariantPrimitiveId::TIMESTAMP_NANOS:
135
2
        static_cast<void>(value.get_timestamp_nanos());
136
2
        return;
137
4
    case VariantPrimitiveId::TIMESTAMP_NTZ_NANOS:
138
4
        static_cast<void>(value.get_timestamp_ntz_nanos());
139
4
        return;
140
0
    case VariantPrimitiveId::UUID:
141
0
        static_cast<void>(value.get_uuid());
142
0
        return;
143
789
    }
144
0
    throw Exception(ErrorCode::CORRUPTION, "VariantField contains an unknown primitive type");
145
789
}
146
147
void require_exact_value(VariantValueRef value, uint32_t depth);
148
149
struct ObjectValueSpan {
150
    size_t offset;
151
    size_t size;
152
};
153
154
250
size_t object_values_offset(VariantValueRef value, uint32_t count) {
155
250
    const uint8_t value_header = static_cast<uint8_t>(value.data[0]) >> VARIANT_VALUE_HEADER_SHIFT;
156
250
    const auto offset_width =
157
250
            static_cast<uint8_t>(((value_header >> VARIANT_OBJECT_OFFSET_SIZE_SHIFT) & 0x03U) + 1);
158
250
    const auto id_width =
159
250
            static_cast<uint8_t>(((value_header >> VARIANT_OBJECT_ID_SIZE_SHIFT) & 0x03U) + 1);
160
250
    const size_t count_width =
161
250
            (value_header & VARIANT_OBJECT_LARGE_MASK) != 0 ? sizeof(uint32_t) : sizeof(uint8_t);
162
250
    return 1 + count_width + static_cast<size_t>(count) * id_width +
163
250
           (static_cast<size_t>(count) + 1) * offset_width;
164
250
}
165
166
250
void require_valid_object(VariantValueRef value, uint32_t depth) {
167
250
    const uint32_t count = value.num_elements();
168
250
    const size_t values_offset = object_values_offset(value, count);
169
250
    const char* values_begin = value.data + values_offset;
170
250
    const size_t values_size = value.size - values_offset;
171
250
    std::vector<ObjectValueSpan> spans;
172
250
    spans.reserve(count);
173
174
250
    StringRef previous_key;
175
566
    for (uint32_t index = 0; index < count; ++index) {
176
320
        uint32_t field_id = 0;
177
320
        const VariantValueRef child = value.object_value_at(index, &field_id);
178
320
        const StringRef key = value.metadata.key_at(field_id);
179
320
        if (index != 0 && previous_key.compare(key) >= 0) {
180
4
            throw Exception(ErrorCode::CORRUPTION,
181
4
                            "VariantField object keys are not strictly ordered at field {}", index);
182
4
        }
183
316
        require_exact_value(child, depth + 1);
184
316
        spans.push_back(
185
316
                {.offset = static_cast<size_t>(child.data - values_begin), .size = child.size});
186
316
        previous_key = key;
187
316
    }
188
189
246
    std::ranges::sort(spans, [](const ObjectValueSpan& left, const ObjectValueSpan& right) {
190
126
        return left.offset < right.offset;
191
126
    });
192
246
    size_t consumed = 0;
193
309
    for (const ObjectValueSpan& span : spans) {
194
309
        if (span.offset < consumed) {
195
6
            throw Exception(ErrorCode::CORRUPTION,
196
6
                            "VariantField object value spans overlap or reuse an offset");
197
6
        }
198
303
        if (span.offset > consumed) {
199
3
            throw Exception(ErrorCode::CORRUPTION,
200
3
                            "VariantField object values contain an unreferenced gap");
201
3
        }
202
300
        consumed += span.size;
203
300
    }
204
237
    if (consumed != values_size) {
205
2
        throw Exception(ErrorCode::CORRUPTION,
206
2
                        "VariantField object values contain unreferenced trailing bytes");
207
2
    }
208
237
}
209
210
842
void require_valid_array(VariantValueRef value, uint32_t depth) {
211
842
    const uint32_t count = value.num_elements();
212
1.70k
    for (uint32_t index = 0; index < count; ++index) {
213
867
        require_exact_value(value.array_at(index), depth + 1);
214
867
    }
215
842
}
216
217
1.93k
void require_exact_value(VariantValueRef value, uint32_t depth) {
218
1.93k
    if (depth > VARIANT_MAX_NESTING_DEPTH) {
219
2
        throw Exception(ErrorCode::CORRUPTION,
220
2
                        "VariantField value exceeds maximum nesting depth {}",
221
2
                        VARIANT_MAX_NESTING_DEPTH);
222
2
    }
223
1.93k
    const size_t encoded_size = value.value_size();
224
1.93k
    if (encoded_size != value.size) {
225
3
        throw Exception(ErrorCode::CORRUPTION,
226
3
                        "VariantField value has {} trailing bytes after its {} byte root",
227
3
                        value.size - encoded_size, encoded_size);
228
3
    }
229
230
1.93k
    switch (value.basic_type()) {
231
789
    case VariantBasicType::PRIMITIVE:
232
789
        require_valid_primitive(value);
233
789
        return;
234
47
    case VariantBasicType::SHORT_STRING:
235
47
        require_valid_utf8(value.get_string(), "short string");
236
47
        return;
237
250
    case VariantBasicType::OBJECT:
238
250
        require_valid_object(value, depth);
239
250
        return;
240
842
    case VariantBasicType::ARRAY:
241
842
        require_valid_array(value, depth);
242
842
        return;
243
1.93k
    }
244
0
    throw Exception(ErrorCode::CORRUPTION, "VariantField contains an unknown value type");
245
1.93k
}
246
247
struct RowSlices {
248
    VariantMetadataRef metadata;
249
    VariantValueRef value;
250
};
251
252
100
RowSlices split_untrusted(StringRef bytes) {
253
100
    require_non_null(bytes, "encoded row");
254
100
    if (bytes.size < METADATA_SIZE_PREFIX) {
255
4
        throw Exception(ErrorCode::CORRUPTION,
256
4
                        "Truncated VariantField metadata-size prefix: need {} bytes, have {}",
257
4
                        METADATA_SIZE_PREFIX, bytes.size);
258
4
    }
259
96
    const uint32_t metadata_size = read_u32(bytes.data);
260
96
    const size_t payload_size = bytes.size - METADATA_SIZE_PREFIX;
261
96
    if (metadata_size > payload_size) {
262
1
        throw Exception(ErrorCode::CORRUPTION,
263
1
                        "VariantField metadata size {} exceeds {} payload bytes", metadata_size,
264
1
                        payload_size);
265
1
    }
266
95
    VariantMetadataRef metadata {.data = bytes.data + METADATA_SIZE_PREFIX, .size = metadata_size};
267
95
    VariantValueRef value {.metadata = metadata,
268
95
                           .data = metadata.data + metadata.size,
269
95
                           .size = payload_size - metadata.size};
270
95
    return {.metadata = metadata, .value = value};
271
96
}
272
273
69
std::unique_ptr<char[]> copy_bytes(StringRef bytes) {
274
69
    auto result = std::make_unique<char[]>(bytes.size);
275
69
    std::memcpy(result.get(), bytes.data, bytes.size);
276
69
    return result;
277
69
}
278
279
6
[[noreturn]] void throw_comparison_not_supported() {
280
6
    throw Exception(ErrorCode::NOT_IMPLEMENTED_ERROR,
281
6
                    "Comparison between VariantField values is not supported");
282
6
}
283
284
} // namespace
285
286
758
void validate_variant_metadata(VariantMetadataRef metadata) {
287
758
    require_non_null({metadata.data, metadata.size}, "metadata");
288
758
    metadata.validate();
289
758
    const uint32_t key_count = metadata.dict_size();
290
1.08k
    for (uint32_t id = 0; id < key_count; ++id) {
291
327
        require_valid_utf8(metadata.key_at(id), "metadata key");
292
327
    }
293
758
}
294
295
761
void validate_variant_payload(VariantValueRef value) {
296
761
    require_non_null({value.data, value.size}, "value");
297
761
    require_exact_value(value, 0);
298
761
}
299
300
VariantField::VariantField(std::unique_ptr<char[]> data, size_t size) noexcept
301
301
        : _data(std::move(data)), _size(size) {}
302
303
3
VariantField::VariantField(const VariantField& other) : _size(other._size) {
304
3
    if (_size != 0) {
305
3
        _data = std::make_unique<char[]>(_size);
306
3
        std::memcpy(_data.get(), other._data.get(), _size);
307
3
    }
308
3
}
309
310
VariantField::VariantField(VariantField&& other) noexcept
311
45
        : _data(std::move(other._data)), _size(std::exchange(other._size, 0)) {}
312
313
2
VariantField& VariantField::operator=(const VariantField& other) {
314
2
    VariantField copy(other);
315
2
    swap(copy);
316
2
    return *this;
317
2
}
318
319
2
VariantField& VariantField::operator=(VariantField&& other) noexcept {
320
2
    if (this != &other) {
321
2
        _data = std::move(other._data);
322
2
        _size = std::exchange(other._size, 0);
323
2
    }
324
2
    return *this;
325
2
}
326
327
251
VariantField VariantField::encode(VariantValueRef value) {
328
251
    if (value.metadata.size > std::numeric_limits<uint32_t>::max()) {
329
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
330
1
                        "VariantField metadata size {} exceeds uint32 limit", value.metadata.size);
331
1
    }
332
250
    if (value.metadata.size > std::numeric_limits<size_t>::max() - METADATA_SIZE_PREFIX) {
333
0
        throw Exception(ErrorCode::INVALID_ARGUMENT,
334
0
                        "VariantField metadata size exceeds the addressable row size");
335
0
    }
336
250
    const size_t value_offset = METADATA_SIZE_PREFIX + value.metadata.size;
337
250
    if (value.size > std::numeric_limits<size_t>::max() - value_offset) {
338
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
339
1
                        "VariantField value size exceeds the addressable row size");
340
1
    }
341
342
249
    validate_variant_metadata(value.metadata);
343
249
    validate_variant_payload(value);
344
249
    const size_t total_size = value_offset + value.size;
345
249
    auto data = std::make_unique<char[]>(total_size);
346
249
    write_u32(data.get(), static_cast<uint32_t>(value.metadata.size));
347
249
    std::memcpy(data.get() + METADATA_SIZE_PREFIX, value.metadata.data, value.metadata.size);
348
249
    std::memcpy(data.get() + value_offset, value.data, value.size);
349
249
    return {std::move(data), total_size};
350
250
}
351
352
100
VariantField VariantField::decode(StringRef bytes) {
353
100
    const RowSlices slices = split_untrusted(bytes);
354
100
    validate_variant_metadata(slices.metadata);
355
100
    validate_variant_payload(slices.value);
356
100
    return {copy_bytes(bytes), bytes.size};
357
100
}
358
359
131k
VariantValueRef VariantField::ref() const {
360
131k
    if (_size == 0) {
361
3
        throw Exception(ErrorCode::INVALID_ARGUMENT,
362
3
                        "Cannot reference an empty or moved-from VariantField");
363
3
    }
364
131k
    DCHECK(_data != nullptr);
365
131k
    DCHECK_GE(_size, METADATA_SIZE_PREFIX);
366
131k
    const uint32_t metadata_size = read_u32(_data.get());
367
131k
    DCHECK_LE(metadata_size, _size - METADATA_SIZE_PREFIX);
368
131k
    VariantMetadataRef metadata {.data = _data.get() + METADATA_SIZE_PREFIX, .size = metadata_size};
369
131k
    return {.metadata = metadata,
370
131k
            .data = metadata.data + metadata.size,
371
131k
            .size = _size - METADATA_SIZE_PREFIX - metadata.size};
372
131k
}
373
374
71
StringRef VariantField::bytes() const noexcept {
375
71
    return {_data.get(), _size};
376
71
}
377
378
1
bool VariantField::operator<(const VariantField&) const {
379
1
    throw_comparison_not_supported();
380
1
}
381
382
1
bool VariantField::operator<=(const VariantField&) const {
383
1
    throw_comparison_not_supported();
384
1
}
385
386
1
bool VariantField::operator==(const VariantField&) const {
387
1
    throw_comparison_not_supported();
388
1
}
389
390
1
bool VariantField::operator!=(const VariantField&) const {
391
1
    throw_comparison_not_supported();
392
1
}
393
394
1
bool VariantField::operator>=(const VariantField&) const {
395
1
    throw_comparison_not_supported();
396
1
}
397
398
1
bool VariantField::operator>(const VariantField&) const {
399
1
    throw_comparison_not_supported();
400
1
}
401
402
2
void VariantField::swap(VariantField& other) noexcept {
403
2
    _data.swap(other._data);
404
2
    std::swap(_size, other._size);
405
2
}
406
407
} // namespace doris