Coverage Report

Created: 2026-07-14 14:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/variant/variant_builder.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_builder.h"
19
20
#include <algorithm>
21
#include <cstdint>
22
#include <cstring>
23
#include <limits>
24
#include <string>
25
#include <utility>
26
27
#include "common/exception.h"
28
#include "util/utf8_check.h"
29
#include "util/variant/variant_block_builder.h"
30
#include "util/variant/variant_encoded_block.h"
31
#include "util/variant/variant_encoding.h"
32
#include "util/variant/variant_scalar_encoding.h"
33
#include "util/variant/variant_tracked_storage.h"
34
35
namespace doris {
36
namespace {
37
38
constexpr uint32_t INVALID_INDEX = std::numeric_limits<uint32_t>::max();
39
constexpr uint64_t FINISHED_ROW_GENERATION = std::numeric_limits<uint64_t>::max();
40
constexpr uint64_t ABORTED_ROW_GENERATION = FINISHED_ROW_GENERATION - 1;
41
constexpr size_t INLINE_SCALAR_CAPACITY = sizeof(uint32_t);
42
constexpr size_t SMALL_OBJECT_SORT_THRESHOLD = 16;
43
44
0
constexpr unsigned __int128 max_decimal38() {
45
0
    unsigned __int128 value = 1;
46
0
    for (uint8_t digit = 0; digit < 38; ++digit) {
47
0
        value *= 10;
48
0
    }
49
0
    return value - 1;
50
0
}
51
52
constexpr unsigned __int128 MAX_DECIMAL38 = max_decimal38();
53
54
template <typename Container, typename Less>
55
538
void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) {
56
538
    if (count < 2) {
57
405
        return;
58
405
    }
59
133
    if (count <= SMALL_OBJECT_SORT_THRESHOLD) {
60
273
        for (size_t index = begin + 1; index < begin + count; ++index) {
61
156
            typename Container::value_type current = entries[index];
62
156
            size_t insertion = index;
63
305
            while (insertion > begin && less(current, entries[insertion - 1])) {
64
149
                entries[insertion] = entries[insertion - 1];
65
149
                --insertion;
66
149
            }
67
156
            entries[insertion] = current;
68
156
        }
69
117
        return;
70
117
    }
71
16
    std::sort(entries.begin() + begin, entries.begin() + begin + count, less);
72
16
}
variant_builder.cpp:_ZN5doris12_GLOBAL__N_119sort_object_entriesISt6vectorINS_21VariantCollectionCore10FinalChildENS_18CustomStdAllocatorIS4_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEZNS3_22validate_import_objectENS_15VariantValueRefEjEUlRKS4_SD_E_EEvRT_mmT0_
Line
Count
Source
55
12
void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) {
56
12
    if (count < 2) {
57
5
        return;
58
5
    }
59
7
    if (count <= SMALL_OBJECT_SORT_THRESHOLD) {
60
14
        for (size_t index = begin + 1; index < begin + count; ++index) {
61
7
            typename Container::value_type current = entries[index];
62
7
            size_t insertion = index;
63
10
            while (insertion > begin && less(current, entries[insertion - 1])) {
64
3
                entries[insertion] = entries[insertion - 1];
65
3
                --insertion;
66
3
            }
67
7
            entries[insertion] = current;
68
7
        }
69
7
        return;
70
7
    }
71
0
    std::sort(entries.begin() + begin, entries.begin() + begin + count, less);
72
0
}
variant_builder.cpp:_ZN5doris12_GLOBAL__N_119sort_object_entriesISt6vectorIjNS_18CustomStdAllocatorIjNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEZNS_21VariantCollectionCore27validate_unique_object_keysERKNS9_9ContainerEEUljjE_EEvRT_mmT0_
Line
Count
Source
55
270
void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) {
56
270
    if (count < 2) {
57
202
        return;
58
202
    }
59
68
    if (count <= SMALL_OBJECT_SORT_THRESHOLD) {
60
140
        for (size_t index = begin + 1; index < begin + count; ++index) {
61
80
            typename Container::value_type current = entries[index];
62
80
            size_t insertion = index;
63
85
            while (insertion > begin && less(current, entries[insertion - 1])) {
64
5
                entries[insertion] = entries[insertion - 1];
65
5
                --insertion;
66
5
            }
67
80
            entries[insertion] = current;
68
80
        }
69
60
        return;
70
60
    }
71
8
    std::sort(entries.begin() + begin, entries.begin() + begin + count, less);
72
8
}
variant_builder.cpp:_ZN5doris12_GLOBAL__N_119sort_object_entriesISt6vectorINS_21VariantCollectionCore10FinalChildENS_18CustomStdAllocatorIS4_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEZNS3_20sort_object_childrenEmmEUlRKS4_SC_E_EEvRT_mmT0_
Line
Count
Source
55
256
void sort_object_entries(Container& entries, size_t begin, size_t count, Less less) {
56
256
    if (count < 2) {
57
198
        return;
58
198
    }
59
58
    if (count <= SMALL_OBJECT_SORT_THRESHOLD) {
60
119
        for (size_t index = begin + 1; index < begin + count; ++index) {
61
69
            typename Container::value_type current = entries[index];
62
69
            size_t insertion = index;
63
210
            while (insertion > begin && less(current, entries[insertion - 1])) {
64
141
                entries[insertion] = entries[insertion - 1];
65
141
                --insertion;
66
141
            }
67
69
            entries[insertion] = current;
68
69
        }
69
50
        return;
70
50
    }
71
8
    std::sort(entries.begin() + begin, entries.begin() + begin + count, less);
72
8
}
73
74
4
unsigned __int128 magnitude(__int128 value) {
75
4
    const auto unsigned_value = static_cast<unsigned __int128>(value);
76
4
    return value < 0 ? ~unsigned_value + 1 : unsigned_value;
77
4
}
78
79
25.9k
uint8_t minimum_unsigned_width(uint64_t value) {
80
25.9k
    if (value <= std::numeric_limits<uint8_t>::max()) {
81
25.6k
        return 1;
82
25.6k
    }
83
311
    if (value <= std::numeric_limits<uint16_t>::max()) {
84
309
        return 2;
85
309
    }
86
2
    if (value <= 0xFFFFFFU) {
87
2
        return 3;
88
2
    }
89
0
    return 4;
90
2
}
91
92
2.05M
void write_unsigned(char*& output, uint64_t value, uint8_t width) noexcept {
93
8.11M
    for (uint8_t byte = 0; byte < width; ++byte) {
94
6.05M
        *output++ = static_cast<char>(value >> (byte * 8));
95
6.05M
    }
96
2.05M
}
97
98
1.01M
void validate_string_ref(StringRef value, const char* description) {
99
1.01M
    if (value.data == nullptr && value.size != 0) {
100
0
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant {} has a null data pointer",
101
0
                        description);
102
0
    }
103
1.01M
}
104
105
24
void validate_import_root(VariantValueRef value) {
106
24
    if (value.metadata.data == nullptr && value.metadata.size != 0) {
107
0
        throw Exception(ErrorCode::CORRUPTION,
108
0
                        "Variant imported metadata has a null data pointer for {} bytes",
109
0
                        value.metadata.size);
110
0
    }
111
24
    if (value.data == nullptr && value.size != 0) {
112
0
        throw Exception(ErrorCode::CORRUPTION,
113
0
                        "Variant imported value has a null data pointer for {} bytes", value.size);
114
0
    }
115
24
    value.metadata.validate();
116
24
}
117
118
199
void require_import_depth(uint32_t depth) {
119
199
    if (depth > VARIANT_MAX_NESTING_DEPTH) {
120
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
121
1
                        "Variant import exceeds maximum nesting depth {}",
122
1
                        VARIANT_MAX_NESTING_DEPTH);
123
1
    }
124
199
}
125
126
198
void require_exact_import_value(VariantValueRef value) {
127
198
    const size_t encoded_size = value.value_size();
128
198
    if (encoded_size != value.size) {
129
1
        throw Exception(ErrorCode::CORRUPTION,
130
1
                        "Variant imported value has {} trailing bytes after its {} byte root",
131
1
                        value.size - encoded_size, encoded_size);
132
1
    }
133
198
}
134
135
29
void require_import_utf8(StringRef value, const char* description) {
136
29
    if (value.size != 0 && !validate_utf8(value.data, value.size)) {
137
3
        throw Exception(ErrorCode::CORRUPTION, "Variant imported {} is not valid UTF-8",
138
3
                        description);
139
3
    }
140
29
}
141
142
} // namespace
143
144
class VariantCollectionCore {
145
public:
146
    enum class NodeKind : uint8_t { SCALAR, INLINE_SCALAR, OBJECT, ARRAY };
147
    enum class State : uint8_t { COLLECTING, COLLECTED, FINISHED, ABORTED };
148
149
    struct Node {
150
0
        Node() noexcept : kind(NodeKind::SCALAR), payload_index(0), payload_size(0) {}
151
        Node(NodeKind kind_, uint32_t payload_index_, uint32_t payload_size_) noexcept
152
28.7k
                : kind(kind_), payload_index(payload_index_), payload_size(payload_size_) {}
153
        Node(std::array<char, INLINE_SCALAR_CAPACITY> inline_bytes_,
154
             uint32_t payload_size_) noexcept
155
1.06M
                : kind(NodeKind::INLINE_SCALAR),
156
1.06M
                  inline_bytes(inline_bytes_),
157
1.06M
                  payload_size(payload_size_) {}
158
159
        NodeKind kind;
160
        union {
161
            uint32_t payload_index;
162
            std::array<char, INLINE_SCALAR_CAPACITY> inline_bytes;
163
        };
164
        uint32_t payload_size;
165
    };
166
    static_assert(sizeof(Node) == 12);
167
168
    struct Child {
169
        uint32_t node_index;
170
        uint32_t temporary_field_id;
171
        uint32_t next;
172
    };
173
174
    struct Container {
175
14.1k
        explicit Container(NodeKind kind_) : kind(kind_) {}
176
177
        NodeKind kind;
178
        uint32_t first_child = INVALID_INDEX;
179
        uint32_t last_child = INVALID_INDEX;
180
        uint32_t child_count = 0;
181
        uint32_t pending_field_id = 0;
182
        uint32_t previous_or_source_token = INVALID_INDEX;
183
        uint32_t previous_child_cursor = INVALID_INDEX;
184
        bool has_pending_field = false;
185
    };
186
187
    struct Checkpoint {
188
        size_t scalar_bytes;
189
        size_t nodes;
190
        size_t containers;
191
        size_t children;
192
        size_t key_references;
193
    };
194
195
    struct CapacitySnapshot {
196
        size_t scalar_bytes;
197
        size_t nodes;
198
        size_t containers;
199
        size_t children;
200
        size_t scope_stack;
201
        size_t object_id_scratch;
202
        size_t key_references;
203
        size_t container_plans;
204
        size_t planned_object_children;
205
    };
206
207
    struct FinalChild {
208
        uint32_t node_index;
209
        uint32_t final_field_id;
210
    };
211
212
    struct ContainerPlan {
213
        size_t encoded_size = 0;
214
        size_t object_children_begin = 0;
215
        uint32_t values_size = 0;
216
        uint8_t count_width = 0;
217
        uint8_t offset_width = 0;
218
        uint8_t id_width = 0;
219
    };
220
221
743
    explicit VariantCollectionCore(VariantMetadataBuilder& metadata_) : metadata(metadata_) {}
222
223
    void reserve(size_t scalar_byte_count, size_t node_count, size_t container_count,
224
334
                 size_t child_count) {
225
334
        scalar_bytes.reserve(scalar_byte_count);
226
334
        nodes.reserve(node_count);
227
334
        containers.reserve(container_count);
228
334
        children.reserve(child_count);
229
334
        scope_stack.reserve(container_count);
230
334
        object_id_scratch.reserve(child_count);
231
334
        key_references.reserve(child_count);
232
334
        container_plans.reserve(container_count);
233
334
        planned_object_children.reserve(child_count);
234
334
    }
235
236
    CapacitySnapshot capacity_snapshot() const noexcept {
237
        return {.scalar_bytes = scalar_bytes.capacity(),
238
                .nodes = nodes.capacity(),
239
                .containers = containers.capacity(),
240
                .children = children.capacity(),
241
                .scope_stack = scope_stack.capacity(),
242
                .object_id_scratch = object_id_scratch.capacity(),
243
                .key_references = key_references.capacity(),
244
                .container_plans = container_plans.capacity(),
245
                .planned_object_children = planned_object_children.capacity()};
246
    }
247
248
80.3k
    Checkpoint checkpoint() const noexcept {
249
80.3k
        return {.scalar_bytes = scalar_bytes.size(),
250
80.3k
                .nodes = nodes.size(),
251
80.3k
                .containers = containers.size(),
252
80.3k
                .children = children.size(),
253
80.3k
                .key_references = key_references.size()};
254
80.3k
    }
255
256
    void begin_collection(DorisVector<uint32_t>* previous_tokens = nullptr,
257
80.3k
                          DorisVector<uint32_t>* pending_tokens = nullptr) noexcept {
258
80.3k
        DCHECK(scope_stack.empty());
259
80.3k
        DCHECK_EQ(active_container, INVALID_INDEX);
260
80.3k
        DCHECK_EQ(previous_tokens == nullptr, pending_tokens == nullptr);
261
80.3k
        DCHECK(pending_tokens == nullptr || pending_tokens->empty());
262
80.3k
        previous_object_tokens = previous_tokens;
263
80.3k
        pending_object_tokens = pending_tokens;
264
80.3k
        root_node = INVALID_INDEX;
265
80.3k
        state = State::COLLECTING;
266
80.3k
    }
267
268
38
    void rollback(const Checkpoint& start) noexcept {
269
38
        DCHECK_LE(start.scalar_bytes, scalar_bytes.size());
270
38
        DCHECK_LE(start.nodes, nodes.size());
271
38
        DCHECK_LE(start.containers, containers.size());
272
38
        DCHECK_LE(start.children, children.size());
273
38
        DCHECK_LE(start.key_references, key_references.size());
274
38
        scalar_bytes.resize(start.scalar_bytes);
275
38
        nodes.resize(start.nodes);
276
38
        containers.erase(containers.begin() + start.containers, containers.end());
277
38
        children.resize(start.children);
278
38
        key_references.resize(start.key_references);
279
38
        scope_stack.clear();
280
38
        active_container = INVALID_INDEX;
281
38
        object_id_scratch.clear();
282
38
        planned_object_children.clear();
283
38
        if (pending_object_tokens != nullptr) {
284
38
            pending_object_tokens->clear();
285
38
        }
286
38
        previous_object_tokens = nullptr;
287
38
        pending_object_tokens = nullptr;
288
38
        root_node = INVALID_INDEX;
289
38
        state = State::ABORTED;
290
38
    }
291
292
80.2k
    void publish_object_links() noexcept {
293
80.2k
        if (pending_object_tokens == nullptr) {
294
0
            return;
295
0
        }
296
80.2k
        DCHECK(previous_object_tokens != nullptr);
297
80.2k
        DCHECK_NE(root_node, INVALID_INDEX);
298
80.2k
        previous_object_tokens->swap(*pending_object_tokens);
299
80.2k
        pending_object_tokens->clear();
300
80.2k
        previous_object_tokens = nullptr;
301
80.2k
        pending_object_tokens = nullptr;
302
80.2k
    }
303
304
80.2k
    void discard_key_references(size_t begin) noexcept {
305
80.2k
        DCHECK_LE(begin, key_references.size());
306
80.2k
        key_references.resize(begin);
307
80.2k
    }
308
309
38
    const uint32_t* key_reference_data(size_t begin) const noexcept {
310
38
        DCHECK_LE(begin, key_references.size());
311
38
        return key_references.empty() ? nullptr : key_references.data() + begin;
312
38
    }
313
314
38
    size_t key_reference_count(size_t begin) const noexcept {
315
38
        DCHECK_LE(begin, key_references.size());
316
38
        return key_references.size() - begin;
317
38
    }
318
319
2.12M
    void ensure_collecting() const {
320
2.12M
        if (state != State::COLLECTING) {
321
1
            throw Exception(ErrorCode::INVALID_ARGUMENT,
322
1
                            "Cannot add to a Variant row builder after collection completes");
323
1
        }
324
2.12M
        if (metadata.is_sealed()) {
325
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
326
0
                            "Cannot add a Variant value after metadata seal");
327
0
        }
328
2.12M
    }
329
330
1.09M
    void ensure_can_add_value() const {
331
1.09M
        ensure_collecting();
332
1.09M
        if (active_container == INVALID_INDEX) {
333
80.4k
            if (root_node != INVALID_INDEX) {
334
0
                throw Exception(ErrorCode::INVALID_ARGUMENT,
335
0
                                "Variant row builder accepts exactly one root value");
336
0
            }
337
80.4k
            return;
338
80.4k
        }
339
340
1.09M
        DCHECK_LT(active_container, containers.size());
341
1.01M
        const Container& parent = containers[active_container];
342
1.01M
        if (parent.child_count == std::numeric_limits<uint32_t>::max()) {
343
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
344
0
                            "Variant container exceeds the uint32 element limit");
345
0
        }
346
1.01M
        if (children.size() == std::numeric_limits<uint32_t>::max()) {
347
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
348
0
                            "Variant encoding unit exceeds the uint32 temporary child limit");
349
0
        }
350
1.01M
        if (parent.kind == NodeKind::OBJECT && !parent.has_pending_field) {
351
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
352
0
                            "Variant object value must be preceded by add_key");
353
0
        }
354
1.01M
    }
355
356
1.09M
    void prepare_value_node() const {
357
1.09M
        ensure_can_add_value();
358
1.09M
        if (nodes.size() == std::numeric_limits<uint32_t>::max()) {
359
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
360
0
                            "Variant encoding unit exceeds the uint32 temporary node limit");
361
0
        }
362
1.09M
    }
363
364
14.5k
    uint32_t prepare_arena_scalar(size_t encoded_size) const {
365
14.5k
        prepare_value_node();
366
14.5k
        if (encoded_size > std::numeric_limits<uint32_t>::max() - scalar_bytes.size()) {
367
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
368
0
                            "Variant encoding unit scalar scratch exceeds the uint32 byte limit");
369
0
        }
370
14.5k
        return static_cast<uint32_t>(scalar_bytes.size());
371
14.5k
    }
372
373
1.08M
    void add_scalar(const VariantScalarEncodingPlan& plan) {
374
1.08M
        if (plan.size() <= INLINE_SCALAR_CAPACITY) {
375
1.06M
            prepare_value_node();
376
1.06M
            std::array<char, INLINE_SCALAR_CAPACITY> inline_bytes {};
377
1.06M
            plan.write(inline_bytes.data(), plan.size());
378
1.06M
            complete_inline_scalar(inline_bytes, plan.size());
379
1.06M
            return;
380
1.06M
        }
381
382
14.5k
        const uint32_t offset = prepare_arena_scalar(plan.size());
383
14.5k
        scalar_bytes.resize(scalar_bytes.size() + plan.size());
384
14.5k
        plan.write(scalar_bytes.data() + offset, plan.size());
385
14.5k
        complete_scalar(offset);
386
14.5k
    }
387
388
1.06M
    void add_null() { add_scalar(VariantScalarEncodingPlan::null_value()); }
389
390
54
    void add_bool(bool value) { add_scalar(VariantScalarEncodingPlan::boolean(value)); }
391
392
13.7k
    void add_int(int64_t value) { add_scalar(VariantScalarEncodingPlan::integer(value)); }
393
394
24
    void add_value(VariantValueRef value) {
395
24
        ensure_can_add_value();
396
24
        planned_object_children.clear();
397
24
        try {
398
24
            validate_import_root(value);
399
24
            if (scope_stack.size() > VARIANT_MAX_NESTING_DEPTH) {
400
0
                throw Exception(ErrorCode::INVALID_ARGUMENT,
401
0
                                "Variant import exceeds maximum nesting depth {}",
402
0
                                VARIANT_MAX_NESTING_DEPTH);
403
0
            }
404
24
            import_node(value, static_cast<uint32_t>(scope_stack.size()));
405
24
        } catch (...) {
406
            // The caller still owns row rollback; only release importer scratch here.
407
10
            planned_object_children.clear();
408
10
            throw;
409
10
        }
410
24
        DCHECK(planned_object_children.empty());
411
14
    }
412
413
42
    void import_primitive(VariantValueRef value) {
414
42
        const VariantPrimitiveId id = value.primitive_id();
415
42
        switch (id) {
416
7
        case VariantPrimitiveId::NULL_VALUE:
417
7
            add_null();
418
7
            return;
419
5
        case VariantPrimitiveId::TRUE_VALUE:
420
9
        case VariantPrimitiveId::FALSE_VALUE:
421
9
            add_bool(value.get_bool());
422
9
            return;
423
7
        case VariantPrimitiveId::INT8:
424
9
        case VariantPrimitiveId::INT16:
425
10
        case VariantPrimitiveId::INT32:
426
11
        case VariantPrimitiveId::INT64:
427
11
            add_int(value.get_int());
428
11
            return;
429
1
        case VariantPrimitiveId::FLOAT:
430
1
            add_scalar(VariantScalarEncodingPlan::float32(value.get_float()));
431
1
            return;
432
1
        case VariantPrimitiveId::DOUBLE:
433
1
            add_scalar(VariantScalarEncodingPlan::float64(value.get_double()));
434
1
            return;
435
1
        case VariantPrimitiveId::DECIMAL4:
436
2
        case VariantPrimitiveId::DECIMAL8:
437
4
        case VariantPrimitiveId::DECIMAL16: {
438
4
            const VariantDecimal decimal = value.get_decimal();
439
4
            if (magnitude(decimal.unscaled) > MAX_DECIMAL38) {
440
1
                throw Exception(ErrorCode::CORRUPTION,
441
1
                                "Variant imported decimal exceeds precision 38");
442
1
            }
443
3
            add_scalar(VariantScalarEncodingPlan::decimal(decimal.unscaled, decimal.scale));
444
3
            return;
445
4
        }
446
1
        case VariantPrimitiveId::DATE:
447
1
            add_scalar(VariantScalarEncodingPlan::date(value.get_date()));
448
1
            return;
449
1
        case VariantPrimitiveId::TIMESTAMP_MICROS:
450
1
            add_scalar(VariantScalarEncodingPlan::timestamp_micros(value.get_timestamp_micros(),
451
1
                                                                   true));
452
1
            return;
453
1
        case VariantPrimitiveId::TIMESTAMP_NTZ_MICROS:
454
1
            add_scalar(VariantScalarEncodingPlan::timestamp_micros(value.get_timestamp_ntz_micros(),
455
1
                                                                   false));
456
1
            return;
457
1
        case VariantPrimitiveId::BINARY:
458
1
            add_scalar(VariantScalarEncodingPlan::binary(value.get_binary()));
459
1
            return;
460
1
        case VariantPrimitiveId::STRING: {
461
1
            const StringRef string = value.get_string();
462
1
            require_import_utf8(string, "string");
463
1
            add_scalar(VariantScalarEncodingPlan::string(string));
464
1
            return;
465
4
        }
466
1
        case VariantPrimitiveId::TIME_NTZ_MICROS:
467
1
            add_scalar(VariantScalarEncodingPlan::time_ntz_micros(value.get_time_ntz_micros()));
468
1
            return;
469
1
        case VariantPrimitiveId::TIMESTAMP_NANOS:
470
1
            add_scalar(
471
1
                    VariantScalarEncodingPlan::timestamp_nanos(value.get_timestamp_nanos(), true));
472
1
            return;
473
1
        case VariantPrimitiveId::TIMESTAMP_NTZ_NANOS:
474
1
            add_scalar(VariantScalarEncodingPlan::timestamp_nanos(value.get_timestamp_ntz_nanos(),
475
1
                                                                  false));
476
1
            return;
477
1
        case VariantPrimitiveId::UUID:
478
1
            add_scalar(VariantScalarEncodingPlan::uuid(value.get_uuid()));
479
1
            return;
480
42
        }
481
42
    }
482
483
14
    size_t object_values_offset(VariantValueRef value, uint32_t count) const noexcept {
484
14
        const uint8_t value_header =
485
14
                static_cast<uint8_t>(value.data[0]) >> VARIANT_VALUE_HEADER_SHIFT;
486
14
        const auto offset_width = static_cast<uint8_t>(
487
14
                ((value_header >> VARIANT_OBJECT_OFFSET_SIZE_SHIFT) & 0x03) + 1);
488
14
        const auto id_width =
489
14
                static_cast<uint8_t>(((value_header >> VARIANT_OBJECT_ID_SIZE_SHIFT) & 0x03) + 1);
490
14
        const uint8_t count_width = (value_header & VARIANT_OBJECT_LARGE_MASK) != 0
491
14
                                            ? sizeof(uint32_t)
492
14
                                            : sizeof(uint8_t);
493
14
        return 1 + count_width + static_cast<size_t>(count) * id_width +
494
14
               (static_cast<size_t>(count) + 1) * offset_width;
495
14
    }
496
497
14
    void validate_import_object(VariantValueRef value, uint32_t count) {
498
14
        const size_t values_offset = object_values_offset(value, count);
499
14
        DCHECK_LE(values_offset, value.size);
500
14
        const char* values_begin = value.data + values_offset;
501
14
        const auto values_size = static_cast<uint32_t>(value.size - values_offset);
502
        // Pass-2 planning is idle during collection, so reuse its retained capacity to validate
503
        // the source object's physical value intervals without adding a per-row scratch buffer.
504
14
        planned_object_children.clear();
505
14
        planned_object_children.reserve(count);
506
507
14
        StringRef previous_key;
508
35
        for (uint32_t index = 0; index < count; ++index) {
509
22
            uint32_t field_id = 0;
510
22
            const VariantValueRef child = value.object_value_at(index, &field_id);
511
22
            const StringRef key = value.metadata.key_at(field_id);
512
22
            require_import_utf8(key, "object key");
513
22
            if (index != 0 && previous_key.compare(key) >= 0) {
514
1
                throw Exception(ErrorCode::CORRUPTION,
515
1
                                "Variant imported object keys are not strictly byte-sorted at "
516
1
                                "field {}",
517
1
                                index);
518
1
            }
519
21
            previous_key = key;
520
21
            const auto begin = static_cast<uint32_t>(child.data - values_begin);
521
21
            DCHECK_LE(child.size, std::numeric_limits<uint32_t>::max());
522
21
            planned_object_children.push_back(
523
21
                    {.node_index = begin, .final_field_id = static_cast<uint32_t>(child.size)});
524
21
        }
525
526
13
        sort_object_entries(planned_object_children, 0, planned_object_children.size(),
527
13
                            [](const FinalChild& left, const FinalChild& right) {
528
7
                                return left.node_index < right.node_index;
529
7
                            });
530
13
        uint32_t expected_begin = 0;
531
19
        for (const FinalChild& interval : planned_object_children) {
532
19
            if (interval.node_index != expected_begin) {
533
2
                throw Exception(ErrorCode::CORRUPTION,
534
2
                                "Variant imported object values are not a complete non-overlapping "
535
2
                                "partition at byte {}",
536
2
                                expected_begin);
537
2
            }
538
17
            expected_begin += interval.final_field_id;
539
17
        }
540
11
        if (expected_begin != values_size) {
541
1
            throw Exception(ErrorCode::CORRUPTION,
542
1
                            "Variant imported object values contain {} unreferenced trailing bytes",
543
1
                            values_size - expected_begin);
544
1
        }
545
10
        planned_object_children.clear();
546
10
    }
547
548
14
    void import_object(VariantValueRef value, uint32_t depth) {
549
14
        const uint32_t count = value.num_elements();
550
14
        validate_import_object(value, count);
551
14
        const uint32_t token = start_container(NodeKind::OBJECT);
552
27
        for (uint32_t index = 0; index < count; ++index) {
553
13
            uint32_t field_id = 0;
554
13
            const VariantValueRef child = value.object_value_at(index, &field_id);
555
13
            add_key(token, value.metadata.key_at(field_id));
556
13
            import_node(child, depth + 1);
557
13
        }
558
14
        finish_container(token, NodeKind::OBJECT);
559
14
    }
560
561
135
    void import_array(VariantValueRef value, uint32_t depth) {
562
135
        const uint32_t count = value.num_elements();
563
135
        const uint32_t token = start_container(NodeKind::ARRAY);
564
297
        for (uint32_t index = 0; index < count; ++index) {
565
162
            import_node(value.array_at(index), depth + 1);
566
162
        }
567
135
        finish_container(token, NodeKind::ARRAY);
568
135
    }
569
570
199
    void import_node(VariantValueRef value, uint32_t depth) {
571
199
        require_import_depth(depth);
572
199
        require_exact_import_value(value);
573
199
        switch (value.basic_type()) {
574
42
        case VariantBasicType::PRIMITIVE:
575
42
            import_primitive(value);
576
42
            return;
577
6
        case VariantBasicType::SHORT_STRING: {
578
6
            const StringRef string = value.get_string();
579
6
            require_import_utf8(string, "string");
580
6
            add_scalar(VariantScalarEncodingPlan::string(string));
581
6
            return;
582
0
        }
583
14
        case VariantBasicType::OBJECT:
584
14
            import_object(value, depth);
585
14
            return;
586
135
        case VariantBasicType::ARRAY:
587
135
            import_array(value, depth);
588
135
            return;
589
199
        }
590
199
    }
591
592
1.09M
    void attach_node(uint32_t node_index) {
593
1.09M
        if (active_container == INVALID_INDEX) {
594
80.4k
            DCHECK_EQ(root_node, INVALID_INDEX);
595
80.4k
            root_node = node_index;
596
80.4k
            if (nodes[node_index].kind == NodeKind::INLINE_SCALAR ||
597
80.4k
                nodes[node_index].kind == NodeKind::SCALAR) {
598
67.8k
                state = State::COLLECTED;
599
67.8k
            }
600
80.4k
            return;
601
80.4k
        }
602
603
1.09M
        DCHECK_LT(active_container, containers.size());
604
1.01M
        Container& parent = containers[active_container];
605
1.01M
        const uint32_t field_id = parent.kind == NodeKind::OBJECT ? parent.pending_field_id : 0;
606
1.01M
        const auto child_index = static_cast<uint32_t>(children.size());
607
1.01M
        children.push_back({node_index, field_id, INVALID_INDEX});
608
1.01M
        if (parent.first_child == INVALID_INDEX) {
609
14.0k
            parent.first_child = child_index;
610
1.00M
        } else {
611
1.00M
            DCHECK_NE(parent.last_child, INVALID_INDEX);
612
1.00M
            children[parent.last_child].next = child_index;
613
1.00M
        }
614
1.01M
        parent.last_child = child_index;
615
1.01M
        ++parent.child_count;
616
1.01M
        parent.has_pending_field = false;
617
1.01M
    }
618
619
14.5k
    void complete_scalar(uint32_t offset) {
620
14.5k
        DCHECK_LE(scalar_bytes.size() - offset, std::numeric_limits<uint32_t>::max());
621
14.5k
        const auto node_index = static_cast<uint32_t>(nodes.size());
622
14.5k
        nodes.emplace_back(NodeKind::SCALAR, offset,
623
14.5k
                           static_cast<uint32_t>(scalar_bytes.size() - offset));
624
14.5k
        attach_node(node_index);
625
14.5k
    }
626
627
    void complete_inline_scalar(const std::array<char, INLINE_SCALAR_CAPACITY>& inline_bytes,
628
1.06M
                                size_t encoded_size) {
629
1.06M
        DCHECK_GT(encoded_size, 0);
630
1.06M
        DCHECK_LE(encoded_size, INLINE_SCALAR_CAPACITY);
631
1.06M
        const auto node_index = static_cast<uint32_t>(nodes.size());
632
1.06M
        nodes.emplace_back(inline_bytes, static_cast<uint32_t>(encoded_size));
633
1.06M
        attach_node(node_index);
634
1.06M
    }
635
636
    void add_inline_scalar(const std::array<char, INLINE_SCALAR_CAPACITY>& inline_bytes,
637
0
                           size_t encoded_size) {
638
0
        prepare_value_node();
639
0
        complete_inline_scalar(inline_bytes, encoded_size);
640
0
    }
641
642
0
    uint32_t start_container(NodeKind kind) {
643
0
        prepare_value_node();
644
0
        if (containers.size() == std::numeric_limits<uint32_t>::max()) {
645
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
646
0
                            "Variant encoding unit exceeds the uint32 temporary container limit");
647
0
        }
648
649
0
        const auto token = static_cast<uint32_t>(containers.size());
650
0
        uint32_t previous_token = INVALID_INDEX;
651
0
        uint32_t previous_child_cursor = INVALID_INDEX;
652
0
        if (kind == NodeKind::OBJECT && pending_object_tokens != nullptr) {
653
0
            DCHECK(previous_object_tokens != nullptr);
654
0
            const size_t object_ordinal = pending_object_tokens->size();
655
0
            if (object_ordinal < previous_object_tokens->size()) {
656
0
                previous_token = (*previous_object_tokens)[object_ordinal];
657
0
                DCHECK_LT(previous_token, token);
658
0
                DCHECK(containers[previous_token].kind == NodeKind::OBJECT);
659
0
                previous_child_cursor = containers[previous_token].first_child;
660
0
            }
661
#ifdef BE_TEST
662
            const size_t old_capacity = pending_object_tokens->capacity();
663
#endif
664
0
            try {
665
0
                pending_object_tokens->push_back(token);
666
0
            } catch (...) {
667
0
                state = State::ABORTED;
668
0
                throw;
669
0
            }
670
#ifdef BE_TEST
671
            object_token_capacity_growths += old_capacity != pending_object_tokens->capacity();
672
#endif
673
0
        }
674
0
        try {
675
0
            containers.emplace_back(kind);
676
0
            containers.back().previous_or_source_token = previous_token;
677
0
            containers.back().previous_child_cursor = previous_child_cursor;
678
0
            const auto node_index = static_cast<uint32_t>(nodes.size());
679
0
            nodes.emplace_back(kind, token, 0);
680
0
            attach_node(node_index);
681
0
            scope_stack.push_back(token);
682
0
            active_container = token;
683
0
        } catch (...) {
684
0
            if (kind == NodeKind::OBJECT && pending_object_tokens != nullptr) {
685
0
                DCHECK(!pending_object_tokens->empty());
686
0
                DCHECK_EQ(pending_object_tokens->back(), token);
687
0
                pending_object_tokens->pop_back();
688
0
            }
689
0
            if (pending_object_tokens != nullptr) {
690
                // Block rows own rollback; legacy rows must stay collecting so abort releases
691
                // their metadata references.
692
0
                state = State::ABORTED;
693
0
            }
694
0
            throw;
695
0
        }
696
0
        return token;
697
0
    }
698
699
1.01M
    void ensure_can_add_key(uint32_t token) const {
700
1.01M
        ensure_collecting();
701
1.01M
        if (token >= containers.size() || active_container != token ||
702
1.01M
            containers[token].kind != NodeKind::OBJECT) {
703
1
            throw Exception(ErrorCode::INVALID_ARGUMENT,
704
1
                            "Variant object scope is not the active scope");
705
1
        }
706
1.01M
        if (containers[token].has_pending_field) {
707
1
            throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object key is missing its value");
708
1
        }
709
1.01M
    }
710
711
1.01M
    void commit_key(uint32_t token, uint32_t temporary_id) noexcept {
712
1.01M
        DCHECK_LT(token, containers.size());
713
1.01M
        Container& object = containers[token];
714
1.01M
        DCHECK(object.kind == NodeKind::OBJECT);
715
1.01M
        DCHECK(!object.has_pending_field);
716
1.01M
        object.pending_field_id = temporary_id;
717
1.01M
        object.has_pending_field = true;
718
1.01M
    }
719
720
1.01M
    void add_key(uint32_t token, StringRef key) {
721
1.01M
        ensure_can_add_key(token);
722
1.01M
        validate_string_ref(key, "metadata key");
723
1.01M
        Container& object = containers[token];
724
725
1.01M
        bool key_hit = false;
726
1.01M
        uint32_t next_previous_child = INVALID_INDEX;
727
1.01M
        uint32_t temporary_id = INVALID_INDEX;
728
1.01M
        if (object.previous_or_source_token != INVALID_INDEX &&
729
1.01M
            object.previous_child_cursor != INVALID_INDEX) {
730
13.0k
            const Child& previous_child = children[object.previous_child_cursor];
731
13.0k
            const StringRef previous_key =
732
13.0k
                    metadata._temporary_key(previous_child.temporary_field_id);
733
13.0k
            if (previous_key.size == key.size &&
734
13.0k
                (key.size == 0 || std::memcmp(previous_key.data, key.data, key.size) == 0)) {
735
13.0k
                key_hit = true;
736
13.0k
                next_previous_child = previous_child.next;
737
13.0k
                temporary_id = previous_child.temporary_field_id;
738
13.0k
            }
739
13.0k
        }
740
1.01M
        if (!key_hit) {
741
1.00M
            temporary_id = metadata.register_key(key);
742
1.00M
        }
743
744
1.01M
        key_references.push_back(temporary_id);
745
1.01M
        metadata._retain_key(temporary_id);
746
1.01M
        commit_key(token, temporary_id);
747
1.01M
        if (key_hit) {
748
13.0k
            object.previous_child_cursor = next_previous_child;
749
1.00M
        } else {
750
1.00M
            object.previous_or_source_token = INVALID_INDEX;
751
1.00M
            object.previous_child_cursor = INVALID_INDEX;
752
1.00M
        }
753
1.01M
    }
754
755
270
    void validate_unique_object_keys(const Container& object) {
756
270
        object_id_scratch.clear();
757
270
        object_id_scratch.reserve(object.child_count);
758
270
        uint32_t child_index = object.first_child;
759
1.00M
        for (uint32_t index = 0; index < object.child_count; ++index) {
760
1.00M
            DCHECK_NE(child_index, INVALID_INDEX);
761
1.00M
            const Child& child = children[child_index];
762
1.00M
            object_id_scratch.push_back(child.temporary_field_id);
763
1.00M
            child_index = child.next;
764
1.00M
        }
765
270
        DCHECK_EQ(child_index, INVALID_INDEX);
766
270
        sort_object_entries(object_id_scratch, 0, object_id_scratch.size(),
767
25.6M
                            [](uint32_t left, uint32_t right) { return left < right; });
768
1.00M
        for (size_t index = 1; index < object_id_scratch.size(); ++index) {
769
1.00M
            if (object_id_scratch[index - 1] == object_id_scratch[index]) {
770
8
                throw Exception(ErrorCode::INVALID_ARGUMENT, "Duplicate Variant object key");
771
8
            }
772
1.00M
        }
773
270
    }
774
775
13.4k
    void finish_container(uint32_t token, NodeKind expected_kind) {
776
13.4k
        ensure_collecting();
777
13.4k
        if (token >= containers.size() || active_container != token ||
778
13.4k
            containers[token].kind != expected_kind) {
779
1
            throw Exception(ErrorCode::INVALID_ARGUMENT,
780
1
                            "Variant container scopes must finish in nesting order");
781
1
        }
782
13.4k
        if (expected_kind == NodeKind::OBJECT && containers[token].has_pending_field) {
783
1
            throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object key is missing its value");
784
1
        }
785
13.4k
        if (expected_kind == NodeKind::OBJECT) {
786
12.5k
            Container& object = containers[token];
787
12.5k
            if (object.previous_or_source_token != INVALID_INDEX &&
788
12.5k
                object.previous_child_cursor == INVALID_INDEX) {
789
12.3k
                DCHECK_LT(object.previous_or_source_token, token);
790
12.3k
                DCHECK_EQ(containers[object.previous_or_source_token].child_count,
791
12.3k
                          object.child_count);
792
#ifdef BE_TEST
793
                ++object_schema_hits;
794
#endif
795
12.3k
            } else {
796
270
                validate_unique_object_keys(object);
797
270
                object.previous_or_source_token = token;
798
#ifdef BE_TEST
799
                ++object_schema_fallbacks;
800
#endif
801
270
            }
802
12.5k
        }
803
13.4k
        DCHECK(!scope_stack.empty());
804
13.4k
        DCHECK_EQ(scope_stack.back(), token);
805
13.4k
        scope_stack.pop_back();
806
13.4k
        if (scope_stack.empty()) {
807
12.6k
            active_container = INVALID_INDEX;
808
12.6k
            state = State::COLLECTED;
809
12.6k
        } else {
810
782
            active_container = scope_stack.back();
811
782
        }
812
13.4k
    }
813
814
256
    void sort_object_children(size_t begin, size_t count) {
815
25.6M
        const auto less_by_id = [](const FinalChild& left, const FinalChild& right) {
816
25.6M
            return left.final_field_id < right.final_field_id;
817
25.6M
        };
818
256
        sort_object_entries(planned_object_children, begin, count, less_by_id);
819
1.00M
        for (size_t index = begin + 1; index < begin + count; ++index) {
820
1.00M
            DCHECK_LT(planned_object_children[index - 1].final_field_id,
821
1.00M
                      planned_object_children[index].final_field_id);
822
1.00M
        }
823
256
    }
824
825
    // Recursive pass-2 is a hot path; splitting it adds call layers in ASAN and production.
826
    // NOLINTNEXTLINE(readability-function-cognitive-complexity,readability-function-size)
827
1.09M
    size_t plan_node(uint32_t node_index) {
828
1.09M
        DCHECK_LT(node_index, nodes.size());
829
1.09M
        const Node& node = nodes[node_index];
830
1.09M
        if (node.kind == NodeKind::INLINE_SCALAR || node.kind == NodeKind::SCALAR) {
831
1.08M
            return node.payload_size;
832
1.08M
        }
833
834
13.3k
        const Container& container = containers[node.payload_index];
835
13.3k
        ContainerPlan& plan = container_plans[node.payload_index];
836
13.3k
        const uint32_t count = container.child_count;
837
13.3k
        plan.count_width =
838
13.3k
                count > std::numeric_limits<uint8_t>::max() ? sizeof(uint32_t) : sizeof(uint8_t);
839
840
13.3k
        if (node.kind == NodeKind::OBJECT) {
841
12.5k
            plan.object_children_begin = planned_object_children.size();
842
12.5k
            DCHECK_NE(container.previous_or_source_token, INVALID_INDEX);
843
12.5k
            if (container.previous_or_source_token != node.payload_index) {
844
12.3k
                const uint32_t source_token = container.previous_or_source_token;
845
12.3k
                DCHECK_LT(source_token, node.payload_index);
846
12.3k
                const Container& source = containers[source_token];
847
12.3k
                const ContainerPlan& source_plan = container_plans[source_token];
848
12.3k
                DCHECK(source.kind == NodeKind::OBJECT);
849
12.3k
                DCHECK_EQ(source.child_count, count);
850
12.3k
                DCHECK_GT(source_plan.encoded_size, 0);
851
852
12.3k
                uint32_t child_index = container.first_child;
853
25.1k
                for (uint32_t index = 0; index < count; ++index) {
854
12.8k
                    DCHECK_NE(child_index, INVALID_INDEX);
855
12.8k
                    const Child& child = children[child_index];
856
12.8k
                    const uint32_t final_id = metadata.final_id(child.temporary_field_id);
857
12.8k
                    DCHECK_LT(final_id, object_id_scratch.size());
858
12.8k
                    DCHECK_EQ(object_id_scratch[final_id], INVALID_INDEX);
859
12.8k
                    object_id_scratch[final_id] = child.node_index;
860
12.8k
                    child_index = child.next;
861
12.8k
                }
862
12.3k
                DCHECK_EQ(child_index, INVALID_INDEX);
863
864
25.1k
                for (uint32_t index = 0; index < count; ++index) {
865
12.8k
                    const uint32_t final_id =
866
12.8k
                            planned_object_children[source_plan.object_children_begin + index]
867
12.8k
                                    .final_field_id;
868
12.8k
                    DCHECK_LT(final_id, object_id_scratch.size());
869
12.8k
                    const uint32_t current_node = object_id_scratch[final_id];
870
12.8k
                    DCHECK_NE(current_node, INVALID_INDEX);
871
12.8k
                    planned_object_children.push_back({current_node, final_id});
872
12.8k
                    object_id_scratch[final_id] = INVALID_INDEX;
873
12.8k
                }
874
#ifdef BE_TEST
875
                ++object_plan_reuses;
876
#endif
877
12.3k
            } else {
878
256
                uint32_t child_index = container.first_child;
879
1.00M
                for (uint32_t index = 0; index < count; ++index) {
880
1.00M
                    DCHECK_NE(child_index, INVALID_INDEX);
881
1.00M
                    const Child& child = children[child_index];
882
1.00M
                    planned_object_children.push_back(
883
1.00M
                            {child.node_index, metadata.final_id(child.temporary_field_id)});
884
1.00M
                    child_index = child.next;
885
1.00M
                }
886
256
                DCHECK_EQ(child_index, INVALID_INDEX);
887
256
                sort_object_children(plan.object_children_begin, count);
888
#ifdef BE_TEST
889
                ++object_plan_fallbacks;
890
#endif
891
256
            }
892
12.5k
        }
893
894
13.3k
        uint32_t values_size = 0;
895
13.3k
        uint32_t array_child_index = container.first_child;
896
1.02M
        for (uint32_t index = 0; index < count; ++index) {
897
1.01M
            uint32_t child_node = 0;
898
1.01M
            if (node.kind == NodeKind::OBJECT) {
899
1.01M
                child_node = planned_object_children[plan.object_children_begin + index].node_index;
900
1.01M
            } else {
901
1.56k
                DCHECK_NE(array_child_index, INVALID_INDEX);
902
1.56k
                child_node = children[array_child_index].node_index;
903
1.56k
                array_child_index = children[array_child_index].next;
904
1.56k
            }
905
1.01M
            const size_t child_size = plan_node(child_node);
906
1.01M
            if (child_size > std::numeric_limits<uint32_t>::max() - values_size) {
907
0
                throw Exception(ErrorCode::INVALID_ARGUMENT,
908
0
                                "Variant container values exceed the uint32 byte limit");
909
0
            }
910
1.01M
            values_size += static_cast<uint32_t>(child_size);
911
1.01M
        }
912
13.3k
        if (node.kind == NodeKind::ARRAY) {
913
825
            DCHECK_EQ(array_child_index, INVALID_INDEX);
914
825
        }
915
13.3k
        plan.values_size = values_size;
916
13.3k
        plan.offset_width = minimum_unsigned_width(values_size);
917
13.3k
        if (node.kind == NodeKind::OBJECT) {
918
12.5k
            const uint32_t maximum_id =
919
12.5k
                    count == 0 ? 0
920
12.5k
                               : planned_object_children[plan.object_children_begin + count - 1]
921
12.5k
                                         .final_field_id;
922
12.5k
            plan.id_width = minimum_unsigned_width(maximum_id);
923
12.5k
        }
924
925
13.3k
        uint64_t encoded_size = 1 + plan.count_width + values_size;
926
13.3k
        encoded_size += (static_cast<uint64_t>(count) + 1) * plan.offset_width;
927
13.3k
        if (node.kind == NodeKind::OBJECT) {
928
12.5k
            encoded_size += static_cast<uint64_t>(count) * plan.id_width;
929
12.5k
        }
930
13.3k
        if (encoded_size > std::numeric_limits<size_t>::max()) {
931
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
932
0
                            "Variant row exceeds the addressable output size");
933
0
        }
934
13.3k
        plan.encoded_size = static_cast<size_t>(encoded_size);
935
13.3k
        return plan.encoded_size;
936
13.3k
    }
937
938
447
    void reset_plan() {
939
447
        container_plans.assign(containers.size(), {});
940
447
        planned_object_children.clear();
941
447
        size_t object_child_count = 0;
942
447
        bool has_object_plan_reuse = false;
943
13.8k
        for (size_t index = 0; index < containers.size(); ++index) {
944
13.3k
            const Container& container = containers[index];
945
13.3k
            if (container.kind != NodeKind::OBJECT) {
946
825
                continue;
947
825
            }
948
13.3k
            DCHECK_NE(container.previous_or_source_token, INVALID_INDEX);
949
12.5k
            has_object_plan_reuse |= container.previous_or_source_token != index;
950
12.5k
            if (container.child_count > planned_object_children.max_size() - object_child_count) {
951
0
                throw Exception(ErrorCode::INVALID_ARGUMENT,
952
0
                                "Variant object plan exceeds the addressable size");
953
0
            }
954
12.5k
            object_child_count += container.child_count;
955
12.5k
        }
956
447
        planned_object_children.reserve(object_child_count);
957
447
        if (has_object_plan_reuse) {
958
10
            object_id_scratch.assign(metadata.num_keys(), INVALID_INDEX);
959
437
        } else {
960
437
            object_id_scratch.clear();
961
437
        }
962
447
    }
963
964
80.4k
    size_t prepare_plan(uint32_t root_index) { return plan_node(root_index); }
965
966
1.01M
    size_t node_encoded_size(uint32_t node_index) const noexcept {
967
1.01M
        DCHECK_LT(node_index, nodes.size());
968
1.01M
        const Node& node = nodes[node_index];
969
1.01M
        return node.kind == NodeKind::INLINE_SCALAR || node.kind == NodeKind::SCALAR
970
1.01M
                       ? node.payload_size
971
1.01M
                       : container_plans[node.payload_index].encoded_size;
972
1.01M
    }
973
974
1.09M
    void write_node(uint32_t node_index, char*& output) const noexcept {
975
1.09M
        DCHECK_LT(node_index, nodes.size());
976
1.09M
        const Node& node = nodes[node_index];
977
1.09M
        if (node.kind == NodeKind::INLINE_SCALAR) {
978
1.06M
            std::memcpy(output, node.inline_bytes.data(), node.payload_size);
979
1.06M
            output += node.payload_size;
980
1.06M
            return;
981
1.06M
        }
982
27.9k
        if (node.kind == NodeKind::SCALAR) {
983
14.5k
            std::memcpy(output, scalar_bytes.data() + node.payload_index, node.payload_size);
984
14.5k
            output += node.payload_size;
985
14.5k
            return;
986
14.5k
        }
987
988
13.3k
        const Container& container = containers[node.payload_index];
989
13.3k
        const ContainerPlan& plan = container_plans[node.payload_index];
990
13.3k
        const uint32_t count = container.child_count;
991
13.3k
        const bool is_large = plan.count_width == sizeof(uint32_t);
992
13.3k
        if (node.kind == NodeKind::OBJECT) {
993
12.5k
            const auto value_header = static_cast<uint8_t>(
994
12.5k
                    ((plan.offset_width - 1) << VARIANT_OBJECT_OFFSET_SIZE_SHIFT) |
995
12.5k
                    ((plan.id_width - 1) << VARIANT_OBJECT_ID_SIZE_SHIFT) |
996
12.5k
                    (is_large ? VARIANT_OBJECT_LARGE_MASK : 0));
997
12.5k
            *output++ = static_cast<char>((value_header << VARIANT_VALUE_HEADER_SHIFT) |
998
12.5k
                                          static_cast<uint8_t>(VariantBasicType::OBJECT));
999
12.5k
            write_unsigned(output, count, plan.count_width);
1000
1.02M
            for (uint32_t index = 0; index < count; ++index) {
1001
1.01M
                write_unsigned(
1002
1.01M
                        output,
1003
1.01M
                        planned_object_children[plan.object_children_begin + index].final_field_id,
1004
1.01M
                        plan.id_width);
1005
1.01M
            }
1006
12.5k
        } else {
1007
825
            const auto value_header = static_cast<uint8_t>(
1008
825
                    ((plan.offset_width - 1) << VARIANT_ARRAY_OFFSET_SIZE_SHIFT) |
1009
825
                    (is_large ? VARIANT_ARRAY_LARGE_MASK : 0));
1010
825
            *output++ = static_cast<char>((value_header << VARIANT_VALUE_HEADER_SHIFT) |
1011
825
                                          static_cast<uint8_t>(VariantBasicType::ARRAY));
1012
825
            write_unsigned(output, count, plan.count_width);
1013
825
        }
1014
1015
13.3k
        uint32_t offset = 0;
1016
13.3k
        write_unsigned(output, offset, plan.offset_width);
1017
13.3k
        uint32_t array_child_index = container.first_child;
1018
1.02M
        for (uint32_t index = 0; index < count; ++index) {
1019
1.01M
            uint32_t child_node = 0;
1020
1.01M
            if (node.kind == NodeKind::OBJECT) {
1021
1.01M
                child_node = planned_object_children[plan.object_children_begin + index].node_index;
1022
1.01M
            } else {
1023
1.56k
                DCHECK_NE(array_child_index, INVALID_INDEX);
1024
1.56k
                child_node = children[array_child_index].node_index;
1025
1.56k
                array_child_index = children[array_child_index].next;
1026
1.56k
            }
1027
1.01M
            offset += static_cast<uint32_t>(node_encoded_size(child_node));
1028
1.01M
            write_unsigned(output, offset, plan.offset_width);
1029
1.01M
        }
1030
13.3k
        DCHECK_EQ(offset, plan.values_size);
1031
13.3k
        if (node.kind == NodeKind::ARRAY) {
1032
825
            DCHECK_EQ(array_child_index, INVALID_INDEX);
1033
825
        }
1034
1035
13.3k
        array_child_index = container.first_child;
1036
1.02M
        for (uint32_t index = 0; index < count; ++index) {
1037
1.01M
            uint32_t child_node = 0;
1038
1.01M
            if (node.kind == NodeKind::OBJECT) {
1039
1.01M
                child_node = planned_object_children[plan.object_children_begin + index].node_index;
1040
1.01M
            } else {
1041
1.56k
                DCHECK_NE(array_child_index, INVALID_INDEX);
1042
1.56k
                child_node = children[array_child_index].node_index;
1043
1.56k
                array_child_index = children[array_child_index].next;
1044
1.56k
            }
1045
1.01M
            write_node(child_node, output);
1046
1.01M
        }
1047
13.3k
        if (node.kind == NodeKind::ARRAY) {
1048
825
            DCHECK_EQ(array_child_index, INVALID_INDEX);
1049
825
        }
1050
13.3k
    }
1051
1052
310
    void release_scratch() noexcept {
1053
310
        release_variant_tracked_container(scalar_bytes);
1054
310
        release_variant_tracked_container(nodes);
1055
310
        release_variant_tracked_container(containers);
1056
310
        release_variant_tracked_container(children);
1057
310
        release_variant_tracked_container(scope_stack);
1058
310
        release_variant_tracked_container(object_id_scratch);
1059
310
        release_variant_tracked_container(key_references);
1060
310
        release_variant_tracked_container(container_plans);
1061
310
        release_variant_tracked_container(planned_object_children);
1062
310
        previous_object_tokens = nullptr;
1063
310
        pending_object_tokens = nullptr;
1064
310
        active_container = INVALID_INDEX;
1065
310
        root_node = INVALID_INDEX;
1066
310
    }
1067
1068
    VariantMetadataBuilder& metadata;
1069
    DorisVector<char> scalar_bytes;
1070
    DorisVector<Node> nodes;
1071
    DorisVector<Container> containers;
1072
    DorisVector<Child> children;
1073
    DorisVector<uint32_t> scope_stack;
1074
    DorisVector<uint32_t> object_id_scratch;
1075
    DorisVector<uint32_t> key_references;
1076
    DorisVector<ContainerPlan> container_plans;
1077
    DorisVector<FinalChild> planned_object_children;
1078
    DorisVector<uint32_t>* previous_object_tokens = nullptr;
1079
    DorisVector<uint32_t>* pending_object_tokens = nullptr;
1080
#ifdef BE_TEST
1081
    size_t object_token_capacity_growths = 0;
1082
    size_t object_schema_hits = 0;
1083
    size_t object_schema_fallbacks = 0;
1084
    size_t object_plan_reuses = 0;
1085
    size_t object_plan_fallbacks = 0;
1086
#endif
1087
    uint32_t active_container = INVALID_INDEX;
1088
    uint32_t root_node = INVALID_INDEX;
1089
    State state = State::COLLECTING;
1090
};
1091
1092
struct VariantBuilder::Impl : VariantCollectionCore {
1093
    Impl(VariantBuilder* owner_, VariantMetadataBuilder& metadata_)
1094
409
            : VariantCollectionCore(metadata_), owner(owner_) {}
1095
1096
183
    void add_scalar(const VariantScalarEncodingPlan& plan) {
1097
183
        VariantCollectionCore::add_scalar(plan);
1098
183
        if (state == State::COLLECTED) {
1099
50
            owner->_complete_collection();
1100
50
        }
1101
183
    }
1102
1103
1.00M
    void add_null() {
1104
1.00M
        VariantCollectionCore::add_null();
1105
1.00M
        if (state == State::COLLECTED) {
1106
8
            owner->_complete_collection();
1107
8
        }
1108
1.00M
    }
1109
1110
24
    void add_bool(bool value) {
1111
24
        VariantCollectionCore::add_bool(value);
1112
24
        if (state == State::COLLECTED) {
1113
4
            owner->_complete_collection();
1114
4
        }
1115
24
    }
1116
1117
107
    void add_int(int64_t value) {
1118
107
        VariantCollectionCore::add_int(value);
1119
107
        if (state == State::COLLECTED) {
1120
15
            owner->_complete_collection();
1121
15
        }
1122
107
    }
1123
1124
13
    void add_value(VariantValueRef value) {
1125
13
        VariantCollectionCore::add_value(value);
1126
13
        if (state == State::COLLECTED) {
1127
2
            owner->_complete_collection();
1128
2
        }
1129
13
    }
1130
1131
639
    void finish_container(uint32_t token, NodeKind expected_kind) {
1132
639
        VariantCollectionCore::finish_container(token, expected_kind);
1133
639
        if (state == State::COLLECTED) {
1134
62
            owner->_complete_collection();
1135
62
        }
1136
639
    }
1137
1138
    VariantBuilder* owner;
1139
};
1140
1141
struct VariantBlockBuilder::Impl {
1142
334
    Impl() = default;
1143
1144
334
    void reserve(const ReserveHint& hint) {
1145
334
        collection.reserve(hint.scalar_bytes, hint.nodes, hint.containers, hint.children);
1146
334
        roots.reserve(hint.rows);
1147
334
        previous_object_tokens.reserve(hint.containers);
1148
334
        pending_object_tokens.reserve(hint.containers);
1149
334
    }
1150
1151
282k
    void ensure_open() const {
1152
282k
        if (terminal) {
1153
4
            throw Exception(ErrorCode::INVALID_ARGUMENT,
1154
4
                            "Variant block builder is already finished");
1155
4
        }
1156
282k
    }
1157
1158
202k
    void ensure_active(uint64_t generation) const {
1159
202k
        ensure_open();
1160
202k
        if (!row_active || generation != active_generation) {
1161
4
            throw Exception(ErrorCode::INVALID_ARGUMENT,
1162
4
                            "Variant block row handle is not the active row");
1163
4
        }
1164
202k
    }
1165
1166
#ifdef BE_TEST
1167
    void observe_collection_growth(const VariantCollectionCore::CapacitySnapshot& before) noexcept {
1168
        const VariantCollectionCore::CapacitySnapshot after = collection.capacity_snapshot();
1169
        counters.scalar_capacity_growths += before.scalar_bytes != after.scalar_bytes;
1170
        counters.node_capacity_growths += before.nodes != after.nodes;
1171
        counters.container_capacity_growths += before.containers != after.containers;
1172
        counters.child_capacity_growths += before.children != after.children;
1173
        counters.scope_stack_capacity_growths += before.scope_stack != after.scope_stack;
1174
        counters.object_id_scratch_capacity_growths +=
1175
                before.object_id_scratch != after.object_id_scratch;
1176
        counters.key_reference_capacity_growths += before.key_references != after.key_references;
1177
        counters.container_plan_capacity_growths += before.container_plans != after.container_plans;
1178
        counters.planned_object_child_capacity_growths +=
1179
                before.planned_object_children != after.planned_object_children;
1180
    }
1181
1182
    void observe_root_growth(size_t before) {
1183
        counters.row_root_capacity_growths += before != roots.capacity();
1184
    }
1185
1186
    void refresh_counters(size_t metadata_capacity, size_t metadata_growths,
1187
                          size_t metadata_unique_keys) const noexcept {
1188
        const VariantCollectionCore::CapacitySnapshot capacity = collection.capacity_snapshot();
1189
        counters.metadata_capacity_growths = metadata_growths;
1190
        counters.metadata_unique_keys = metadata_unique_keys;
1191
        counters.metadata_key_capacity = metadata_capacity;
1192
        counters.scalar_byte_capacity = capacity.scalar_bytes;
1193
        counters.node_capacity = capacity.nodes;
1194
        counters.container_capacity = capacity.containers;
1195
        counters.child_capacity = capacity.children;
1196
        counters.scope_stack_capacity = capacity.scope_stack;
1197
        counters.object_id_scratch_capacity = capacity.object_id_scratch;
1198
        counters.key_reference_capacity = capacity.key_references;
1199
        counters.container_plan_capacity = capacity.container_plans;
1200
        counters.planned_object_child_capacity = capacity.planned_object_children;
1201
        counters.row_root_capacity = roots.capacity();
1202
        counters.object_token_capacity_growths = collection.object_token_capacity_growths;
1203
        counters.object_schema_hits = collection.object_schema_hits;
1204
        counters.object_schema_fallbacks = collection.object_schema_fallbacks;
1205
        counters.object_plan_reuses = collection.object_plan_reuses;
1206
        counters.object_plan_fallbacks = collection.object_plan_fallbacks;
1207
        counters.previous_object_token_capacity = previous_object_tokens.capacity();
1208
        counters.pending_object_token_capacity = pending_object_tokens.capacity();
1209
    }
1210
1211
    mutable TestCounters counters;
1212
    VariantCollectionCore::CapacitySnapshot row_capacity_start {};
1213
#endif
1214
1215
    VariantMetadataBuilder metadata;
1216
    VariantCollectionCore collection {metadata};
1217
    DorisVector<uint32_t> roots;
1218
    DorisVector<uint32_t> previous_object_tokens;
1219
    DorisVector<uint32_t> pending_object_tokens;
1220
    VariantCollectionCore::Checkpoint row_start {};
1221
    uint64_t current_generation = 0;
1222
    uint64_t active_generation = 0;
1223
    bool row_active = false;
1224
    bool terminal = false;
1225
#ifdef BE_TEST
1226
    bool counters_finalized = false;
1227
#endif
1228
};
1229
1230
272
VariantBlockBuilder::VariantBlockBuilder() : VariantBlockBuilder(ReserveHint {}) {}
1231
1232
334
VariantBlockBuilder::VariantBlockBuilder(ReserveHint hint) : _impl(std::make_unique<Impl>()) {
1233
334
    _impl->metadata._reserve_keys(hint.metadata_keys);
1234
334
    _impl->reserve(hint);
1235
#ifdef BE_TEST
1236
    _impl->refresh_counters(_impl->metadata._key_capacity(),
1237
                            _impl->metadata._key_capacity_growths(), _impl->metadata.num_keys());
1238
#endif
1239
334
}
1240
1241
334
VariantBlockBuilder::~VariantBlockBuilder() {
1242
334
    if (_impl != nullptr && _impl->row_active) {
1243
0
        _abort_row_noexcept(_impl->active_generation);
1244
0
    }
1245
334
}
1246
1247
2.10k
void VariantBlockBuilder::_add_scalar(uint64_t generation, const VariantScalarEncodingPlan& plan) {
1248
2.10k
    _impl->ensure_active(generation);
1249
2.10k
    _impl->collection.add_scalar(plan);
1250
2.10k
}
1251
1252
66.6k
void VariantBlockBuilder::_add_null(uint64_t generation) {
1253
66.6k
    _impl->ensure_active(generation);
1254
66.6k
    _impl->collection.add_null();
1255
66.6k
}
1256
1257
22
void VariantBlockBuilder::_add_bool(uint64_t generation, bool value) {
1258
22
    _impl->ensure_active(generation);
1259
22
    _impl->collection.add_bool(value);
1260
22
}
1261
1262
13.6k
void VariantBlockBuilder::_add_int(uint64_t generation, int64_t value) {
1263
13.6k
    _impl->ensure_active(generation);
1264
13.6k
    _impl->collection.add_int(value);
1265
13.6k
}
1266
1267
11
void VariantBlockBuilder::_add_value(uint64_t generation, VariantValueRef value) {
1268
11
    _impl->ensure_active(generation);
1269
11
    _impl->collection.add_value(value);
1270
11
}
1271
1272
13.2k
uint32_t VariantBlockBuilder::_start_container(uint64_t generation, bool object) {
1273
13.2k
    _impl->ensure_active(generation);
1274
13.2k
    return _impl->collection.start_container(object ? VariantCollectionCore::NodeKind::OBJECT
1275
13.2k
                                                    : VariantCollectionCore::NodeKind::ARRAY);
1276
13.2k
}
1277
1278
13.5k
void VariantBlockBuilder::_add_key(uint64_t generation, uint32_t token, StringRef key) {
1279
13.5k
    _impl->ensure_active(generation);
1280
13.5k
    _impl->collection.add_key(token, key);
1281
13.5k
}
1282
1283
12.7k
void VariantBlockBuilder::_finish_container(uint64_t generation, uint32_t token, bool object) {
1284
12.7k
    _impl->ensure_active(generation);
1285
12.7k
    _impl->collection.finish_container(token, object ? VariantCollectionCore::NodeKind::OBJECT
1286
12.7k
                                                     : VariantCollectionCore::NodeKind::ARRAY);
1287
12.7k
}
1288
1289
80.2k
void VariantBlockBuilder::_finish_row(uint64_t generation) {
1290
80.2k
    _impl->ensure_active(generation);
1291
80.2k
    if (_impl->collection.state == VariantCollectionCore::State::COLLECTING) {
1292
0
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Cannot finish an incomplete Variant row");
1293
0
    }
1294
80.2k
    DCHECK(_impl->collection.state == VariantCollectionCore::State::COLLECTED);
1295
#ifdef BE_TEST
1296
    const size_t root_capacity = _impl->roots.capacity();
1297
#endif
1298
80.2k
    _impl->roots.push_back(_impl->collection.root_node);
1299
#ifdef BE_TEST
1300
    _impl->observe_root_growth(root_capacity);
1301
#endif
1302
80.2k
    _impl->metadata._complete_row();
1303
80.2k
    _impl->collection.discard_key_references(_impl->row_start.key_references);
1304
80.2k
    _impl->collection.publish_object_links();
1305
80.2k
    _impl->collection.state = VariantCollectionCore::State::FINISHED;
1306
#ifdef BE_TEST
1307
    _impl->observe_collection_growth(_impl->row_capacity_start);
1308
#endif
1309
80.2k
    _impl->row_active = false;
1310
80.2k
}
1311
1312
80.3k
void VariantBlockBuilder::_abort_row_noexcept(uint64_t generation) noexcept {
1313
80.3k
    if (!_impl->row_active || generation != _impl->active_generation) {
1314
80.2k
        return;
1315
80.2k
    }
1316
38
    _impl->metadata._abort_row(
1317
38
            _impl->collection.key_reference_data(_impl->row_start.key_references),
1318
38
            _impl->collection.key_reference_count(_impl->row_start.key_references), true);
1319
38
    _impl->collection.rollback(_impl->row_start);
1320
#ifdef BE_TEST
1321
    _impl->observe_collection_growth(_impl->row_capacity_start);
1322
#endif
1323
38
    _impl->row_active = false;
1324
38
}
1325
1326
9
void VariantBlockBuilder::_abort_row(uint64_t generation) {
1327
9
    _impl->ensure_active(generation);
1328
9
    _abort_row_noexcept(generation);
1329
9
}
1330
1331
80.3k
VariantBlockBuilder::Row VariantBlockBuilder::begin_row() {
1332
80.3k
    _impl->ensure_open();
1333
80.3k
    if (_impl->row_active) {
1334
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1335
1
                        "Variant block builder already has an active row");
1336
1
    }
1337
80.3k
    if (_impl->current_generation >= ABORTED_ROW_GENERATION - 1) {
1338
0
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1339
0
                        "Variant block row generation exceeds the uint64 limit");
1340
0
    }
1341
80.3k
    ++_impl->current_generation;
1342
80.3k
    DCHECK(_impl->pending_object_tokens.empty());
1343
#ifdef BE_TEST
1344
    _impl->row_capacity_start = _impl->collection.capacity_snapshot();
1345
#endif
1346
80.3k
    _impl->collection.begin_collection(&_impl->previous_object_tokens,
1347
80.3k
                                       &_impl->pending_object_tokens);
1348
80.3k
    _impl->row_start = _impl->collection.checkpoint();
1349
80.3k
    _impl->metadata._begin_row();
1350
80.3k
    _impl->active_generation = _impl->current_generation;
1351
80.3k
    _impl->row_active = true;
1352
80.3k
    return {this, _impl->active_generation};
1353
80.3k
}
1354
1355
313
VariantEncodedBlock VariantBlockBuilder::finish_block() {
1356
313
    _impl->ensure_open();
1357
313
    if (_impl->row_active) {
1358
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1359
1
                        "Cannot finish a Variant block while a row is active");
1360
1
    }
1361
312
    _impl->terminal = true;
1362
312
    _impl->metadata.seal();
1363
#ifdef BE_TEST
1364
    const VariantCollectionCore::CapacitySnapshot before = _impl->collection.capacity_snapshot();
1365
#endif
1366
312
    _impl->collection.reset_plan();
1367
1368
312
    DorisVector<uint32_t> offsets;
1369
312
    if (_impl->roots.size() == offsets.max_size()) {
1370
0
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1371
0
                        "Variant block row offsets exceed the addressable size");
1372
0
    }
1373
312
    offsets.reserve(_impl->roots.size() + 1);
1374
312
    offsets.push_back(0);
1375
312
    uint32_t total_size = 0;
1376
80.2k
    for (uint32_t root : _impl->roots) {
1377
80.2k
        const size_t row_size = _impl->collection.prepare_plan(root);
1378
80.2k
        if (row_size > std::numeric_limits<uint32_t>::max() - total_size) {
1379
0
            throw Exception(ErrorCode::INVALID_ARGUMENT,
1380
0
                            "Variant block values exceed the ColumnString uint32 byte limit");
1381
0
        }
1382
80.2k
        total_size += static_cast<uint32_t>(row_size);
1383
80.2k
        offsets.push_back(total_size);
1384
80.2k
    }
1385
#ifdef BE_TEST
1386
    _impl->observe_collection_growth(before);
1387
#endif
1388
312
    auto storage = std::make_unique<VariantEncodedBlockStorage>();
1389
312
    storage->offsets = std::move(offsets);
1390
312
    storage->values.resize(total_size);
1391
312
    char* output = storage->values.data();
1392
80.2k
    for (uint32_t root : _impl->roots) {
1393
80.2k
        _impl->collection.write_node(root, output);
1394
80.2k
    }
1395
312
    DCHECK_EQ(output, storage->values.data() + storage->values.size());
1396
1397
#ifdef BE_TEST
1398
    _impl->refresh_counters(_impl->metadata._key_capacity(),
1399
                            _impl->metadata._key_capacity_growths(), _impl->metadata.num_keys());
1400
    _impl->counters_finalized = true;
1401
#endif
1402
312
    _impl->metadata._move_encoded_metadata(*storage);
1403
312
    _impl->collection.release_scratch();
1404
312
    release_variant_tracked_container(_impl->roots);
1405
312
    release_variant_tracked_container(_impl->previous_object_tokens);
1406
312
    release_variant_tracked_container(_impl->pending_object_tokens);
1407
312
    return VariantEncodedBlock(std::move(storage));
1408
312
}
1409
1410
#ifdef BE_TEST
1411
const VariantBlockBuilder::TestCounters& VariantBlockBuilder::test_counters() const noexcept {
1412
    if (!_impl->counters_finalized) {
1413
        _impl->refresh_counters(_impl->metadata._key_capacity(),
1414
                                _impl->metadata._key_capacity_growths(),
1415
                                _impl->metadata.num_keys());
1416
    }
1417
    return _impl->counters;
1418
}
1419
#endif
1420
1421
VariantBuilder::ObjectScope::ObjectScope(VariantBuilder* builder, uint32_t token) noexcept
1422
48
        : _builder(builder), _token(token) {}
1423
1424
VariantBuilder::ObjectScope::ObjectScope(ObjectScope&& other) noexcept
1425
0
        : _builder(std::exchange(other._builder, nullptr)), _token(other._token) {}
1426
1427
1.00M
void VariantBuilder::ObjectScope::add_key(StringRef key) {
1428
1.00M
    if (_builder == nullptr) {
1429
0
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object scope is already finished");
1430
0
    }
1431
1.00M
    _builder->_add_key(_token, key);
1432
1.00M
}
1433
1434
47
void VariantBuilder::ObjectScope::finish() {
1435
47
    if (_builder == nullptr) {
1436
1
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant object scope is already finished");
1437
1
    }
1438
46
    _builder->_finish_object(_token);
1439
46
    _builder = nullptr;
1440
46
}
1441
1442
VariantBuilder::ArrayScope::ArrayScope(VariantBuilder* builder, uint32_t token) noexcept
1443
693
        : _builder(builder), _token(token) {}
1444
1445
VariantBuilder::ArrayScope::ArrayScope(ArrayScope&& other) noexcept
1446
714
        : _builder(std::exchange(other._builder, nullptr)), _token(other._token) {}
1447
1448
593
void VariantBuilder::ArrayScope::finish() {
1449
593
    if (_builder == nullptr) {
1450
0
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant array scope is already finished");
1451
0
    }
1452
593
    _builder->_finish_array(_token);
1453
593
    _builder = nullptr;
1454
593
}
1455
1456
VariantBuilder::VariantBuilder(VariantMetadataBuilder& metadata)
1457
409
        : _impl(std::make_unique<Impl>(this, metadata)) {
1458
409
    metadata._begin_row();
1459
409
}
1460
1461
408
VariantBuilder::~VariantBuilder() {
1462
408
    _abort_noexcept();
1463
408
}
1464
1465
1.00M
void VariantBuilder::add_null() {
1466
1.00M
    _impl->add_null();
1467
1.00M
}
1468
1469
24
void VariantBuilder::add_bool(bool value) {
1470
24
    _impl->add_bool(value);
1471
24
}
1472
1473
107
void VariantBuilder::add_int(int64_t value) {
1474
107
    _impl->add_int(value);
1475
107
}
1476
1477
10
void VariantBuilder::add_float(float value) {
1478
10
    _impl->add_scalar(VariantScalarEncodingPlan::float32(value));
1479
10
}
1480
1481
9
void VariantBuilder::add_double(double value) {
1482
9
    _impl->add_scalar(VariantScalarEncodingPlan::float64(value));
1483
9
}
1484
1485
21
void VariantBuilder::add_decimal(__int128 unscaled, uint8_t scale) {
1486
21
    _impl->add_scalar(VariantScalarEncodingPlan::decimal(unscaled, scale));
1487
21
}
1488
1489
35
void VariantBuilder::add_decimal(__int128 unscaled, uint8_t scale, uint8_t width) {
1490
35
    if (width != 4 && width != 8 && width != 16) {
1491
4
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1492
4
                        "Variant decimal width {} must be one of 4, 8, or 16", width);
1493
4
    }
1494
31
    _impl->add_scalar(VariantScalarEncodingPlan::decimal(unscaled, scale, width));
1495
31
}
1496
1497
6
void VariantBuilder::add_date(int32_t days_since_epoch) {
1498
6
    _impl->add_scalar(VariantScalarEncodingPlan::date(days_since_epoch));
1499
6
}
1500
1501
17
void VariantBuilder::add_timestamp_micros(int64_t value, bool utc_adjusted) {
1502
17
    _impl->add_scalar(VariantScalarEncodingPlan::timestamp_micros(value, utc_adjusted));
1503
17
}
1504
1505
17
void VariantBuilder::add_timestamp_nanos(int64_t value, bool utc_adjusted) {
1506
17
    _impl->add_scalar(VariantScalarEncodingPlan::timestamp_nanos(value, utc_adjusted));
1507
17
}
1508
1509
7
void VariantBuilder::add_time_ntz_micros(int64_t value) {
1510
7
    _impl->add_scalar(VariantScalarEncodingPlan::time_ntz_micros(value));
1511
7
}
1512
1513
9
void VariantBuilder::add_binary(StringRef value) {
1514
9
    _impl->add_scalar(VariantScalarEncodingPlan::binary(value));
1515
9
}
1516
1517
41
void VariantBuilder::add_string(StringRef value) {
1518
41
    _impl->add_scalar(VariantScalarEncodingPlan::string(value));
1519
41
}
1520
1521
6
void VariantBuilder::add_uuid(const std::array<uint8_t, 16>& value) {
1522
6
    _impl->add_scalar(VariantScalarEncodingPlan::uuid(value));
1523
6
}
1524
1525
16
void VariantBuilder::add_largeint(__int128 value) {
1526
16
    _impl->add_scalar(VariantScalarEncodingPlan::largeint(value));
1527
16
}
1528
1529
13
void VariantBuilder::add_value(VariantValueRef value) {
1530
13
    _impl->add_value(value);
1531
13
}
1532
1533
48
VariantBuilder::ObjectScope VariantBuilder::start_object() {
1534
48
    return {this, _start_object()};
1535
48
}
1536
1537
693
VariantBuilder::ArrayScope VariantBuilder::start_array() {
1538
693
    return {this, _start_array()};
1539
693
}
1540
1541
48
uint32_t VariantBuilder::_start_object() {
1542
48
    return _impl->start_container(Impl::NodeKind::OBJECT);
1543
48
}
1544
1545
693
uint32_t VariantBuilder::_start_array() {
1546
693
    return _impl->start_container(Impl::NodeKind::ARRAY);
1547
693
}
1548
1549
1.00M
void VariantBuilder::_add_key(uint32_t token, StringRef key) {
1550
1.00M
    _impl->add_key(token, key);
1551
1.00M
}
1552
1553
46
void VariantBuilder::_finish_object(uint32_t token) {
1554
46
    _impl->finish_container(token, Impl::NodeKind::OBJECT);
1555
46
}
1556
1557
593
void VariantBuilder::_finish_array(uint32_t token) {
1558
593
    _impl->finish_container(token, Impl::NodeKind::ARRAY);
1559
593
}
1560
1561
141
void VariantBuilder::_complete_collection() {
1562
141
    DCHECK(_impl->state == Impl::State::COLLECTED);
1563
141
    _impl->metadata._complete_row();
1564
141
}
1565
1566
263
void VariantBuilder::abort() {
1567
263
    if (_impl->metadata.is_sealed()) {
1568
1
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1569
1
                        "Cannot abort a Variant row after metadata seal");
1570
1
    }
1571
262
    if (_impl->state == Impl::State::ABORTED) {
1572
0
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant row is already aborted");
1573
0
    }
1574
262
    if (_impl->state == Impl::State::FINISHED) {
1575
0
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant row is already finished");
1576
0
    }
1577
262
    _abort_noexcept();
1578
262
}
1579
1580
670
void VariantBuilder::_abort_noexcept() noexcept {
1581
670
    if (_impl == nullptr ||
1582
670
        (_impl->state != Impl::State::COLLECTING && _impl->state != Impl::State::COLLECTED) ||
1583
670
        _impl->metadata.is_sealed()) {
1584
399
        return;
1585
399
    }
1586
271
    const bool was_collecting = _impl->state == Impl::State::COLLECTING;
1587
271
    _impl->metadata._abort_row(_impl->key_references.data(), _impl->key_references.size(),
1588
271
                               was_collecting);
1589
271
    _impl->state = Impl::State::ABORTED;
1590
271
}
1591
1592
392
void VariantBuilder::finish_row(std::string& destination) {
1593
392
    if (_impl->state == Impl::State::FINISHED) {
1594
1
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Variant row is already finished");
1595
1
    }
1596
391
    if (_impl->state == Impl::State::ABORTED) {
1597
251
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Cannot finish an aborted Variant row");
1598
251
    }
1599
140
    if (_impl->state == Impl::State::COLLECTING) {
1600
1
        throw Exception(ErrorCode::INVALID_ARGUMENT, "Cannot finish an incomplete Variant row");
1601
1
    }
1602
139
    if (!_impl->metadata.is_sealed()) {
1603
2
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1604
2
                        "Variant metadata must be sealed before finishing a row");
1605
2
    }
1606
1607
137
    _impl->reset_plan();
1608
137
    const size_t encoded_size = _impl->prepare_plan(_impl->root_node);
1609
137
    if (encoded_size > destination.max_size() - destination.size()) {
1610
0
        throw Exception(ErrorCode::INVALID_ARGUMENT,
1611
0
                        "Variant destination exceeds the addressable string size");
1612
0
    }
1613
1614
137
    const size_t old_size = destination.size();
1615
137
    destination.resize(old_size + encoded_size);
1616
137
    char* output = destination.data() + old_size;
1617
137
    _impl->write_node(_impl->root_node, output);
1618
137
    DCHECK_EQ(output, destination.data() + destination.size());
1619
137
    _impl->state = Impl::State::FINISHED;
1620
137
}
1621
1622
0
bool VariantBuilder::is_finished() const noexcept {
1623
0
    return _impl->state == Impl::State::FINISHED;
1624
0
}
1625
1626
} // namespace doris