Coverage Report

Created: 2026-08-14 01:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/common/variant_util.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 "exec/common/variant_util.h"
19
20
#include <fmt/format.h>
21
#include <gen_cpp/FrontendService.h>
22
#include <gen_cpp/FrontendService_types.h>
23
#include <gen_cpp/HeartbeatService_types.h>
24
#include <gen_cpp/MasterService_types.h>
25
#include <gen_cpp/Status_types.h>
26
#include <gen_cpp/Types_types.h>
27
#include <glog/logging.h>
28
#include <rapidjson/document.h>
29
#include <rapidjson/stringbuffer.h>
30
#include <rapidjson/writer.h>
31
#include <simdjson/simdjson.h> // IWYU pragma: keep
32
#include <unicode/uchar.h>
33
34
#include <algorithm>
35
#include <cassert>
36
#include <cstddef>
37
#include <cstdint>
38
#include <cstring>
39
#include <list>
40
#include <memory>
41
#include <mutex>
42
#include <optional>
43
#include <ostream>
44
#include <ranges>
45
#include <set>
46
#include <stack>
47
#include <string>
48
#include <string_view>
49
#include <unordered_map>
50
#include <utility>
51
#include <vector>
52
53
#include "common/config.h"
54
#include "common/status.h"
55
#include "core/assert_cast.h"
56
#include "core/block/block.h"
57
#include "core/block/column_numbers.h"
58
#include "core/block/column_with_type_and_name.h"
59
#include "core/column/column.h"
60
#include "core/column/column_array.h"
61
#include "core/column/column_map.h"
62
#include "core/column/column_nullable.h"
63
#include "core/column/column_string.h"
64
#include "core/column/column_variant.h"
65
#include "core/column/variant_v2/column_variant_v2.h"
66
#include "core/data_type/data_type.h"
67
#include "core/data_type/data_type_array.h"
68
#include "core/data_type/data_type_factory.hpp"
69
#include "core/data_type/data_type_jsonb.h"
70
#include "core/data_type/data_type_nullable.h"
71
#include "core/data_type/data_type_string.h"
72
#include "core/data_type/data_type_variant.h"
73
#include "core/data_type/data_type_variant_v2.h"
74
#include "core/data_type/define_primitive_type.h"
75
#include "core/data_type/get_least_supertype.h"
76
#include "core/data_type/primitive_type.h"
77
#include "core/data_type/storage_field_type.h"
78
#include "core/field.h"
79
#include "core/typeid_cast.h"
80
#include "core/types.h"
81
#include "exec/common/field_visitors.h"
82
#include "exec/common/sip_hash.h"
83
#include "exprs/function/function.h"
84
#include "exprs/function/simple_function_factory.h"
85
#include "exprs/function_context.h"
86
#include "exprs/json_functions.h"
87
#include "re2/re2.h"
88
#include "runtime/exec_env.h"
89
#include "runtime/runtime_state.h"
90
#include "storage/olap_common.h"
91
#include "storage/rowset/beta_rowset.h"
92
#include "storage/rowset/rowset.h"
93
#include "storage/rowset/rowset_fwd.h"
94
#include "storage/segment/segment_loader.h"
95
#include "storage/segment/variant/nested_group_path.h"
96
#include "storage/segment/variant/variant_column_reader.h"
97
#include "storage/segment/variant/variant_column_writer_impl.h"
98
#include "storage/tablet/tablet.h"
99
#include "storage/tablet/tablet_fwd.h"
100
#include "storage/tablet/tablet_schema.h"
101
#include "util/client_cache.h"
102
#include "util/defer_op.h"
103
#include "util/json/json_parser.h"
104
#include "util/json/path_in_data.h"
105
#include "util/json/simd_json_parser.h"
106
#include "util/jsonb_utils.h"
107
108
namespace doris::variant_util {
109
110
namespace {
111
112
80
PathInData make_full_subcolumn_path(const TabletColumnPtr& parent_column, std::string_view path) {
113
80
    if (!path.empty()) {
114
74
        return PathInData(parent_column->name_lower_case() + "." + std::string(path));
115
74
    }
116
117
    // Keep the empty JSON key as a real path part. The variant root is `parts.empty()`;
118
    // an empty key is `parts.size() == 1 && parts[0].key.empty()` after popping root.
119
6
    PathInDataBuilder builder;
120
6
    return builder.append(parent_column->name_lower_case(), false).append("", false).build();
121
80
}
122
123
void append_empty_key_subcolumn_from_stats(TabletSchema::PathsSetInfo& paths_set_info,
124
                                           const TabletColumnPtr& parent_column,
125
6
                                           TabletSchemaSPtr& output_schema) {
126
6
    if (!paths_set_info.sub_path_set.contains("") || paths_set_info.sparse_path_set.contains("") ||
127
6
        paths_set_info.subcolumn_indexes.contains("")) {
128
5
        return;
129
5
    }
130
131
1
    auto column_name = parent_column->name_lower_case() + ".";
132
1
    auto column_path = make_full_subcolumn_path(parent_column, "");
133
134
1
    TabletColumn subcolumn;
135
1
    subcolumn.set_name(column_name);
136
1
    subcolumn.set_type(FieldType::OLAP_FIELD_TYPE_VARIANT);
137
1
    subcolumn.set_parent_unique_id(parent_column->unique_id());
138
1
    subcolumn.set_path_info(column_path);
139
1
    subcolumn.set_aggregation_method(parent_column->aggregation());
140
1
    subcolumn.set_variant_max_subcolumns_count(parent_column->variant_max_subcolumns_count());
141
1
    subcolumn.set_variant_enable_doc_mode(parent_column->variant_enable_doc_mode());
142
1
    subcolumn.set_is_nullable(true);
143
1
    output_schema->append_column(subcolumn);
144
1
}
145
146
} // namespace
147
148
279
inline void append_escaped_regex_char(std::string* regex_output, char ch) {
149
279
    switch (ch) {
150
11
    case '.':
151
13
    case '^':
152
15
    case '$':
153
17
    case '+':
154
22
    case '*':
155
24
    case '?':
156
26
    case '(':
157
28
    case ')':
158
30
    case '|':
159
32
    case '{':
160
34
    case '}':
161
36
    case '[':
162
36
    case ']':
163
40
    case '\\':
164
40
        regex_output->push_back('\\');
165
40
        regex_output->push_back(ch);
166
40
        break;
167
239
    default:
168
239
        regex_output->push_back(ch);
169
239
        break;
170
279
    }
171
279
}
172
173
// Small LRU to cap compiled glob patterns
174
constexpr size_t kGlobRegexCacheCapacity = 256;
175
176
struct GlobRegexCacheEntry {
177
    std::shared_ptr<RE2> re2;
178
    std::list<std::string>::iterator lru_it;
179
};
180
181
static std::mutex g_glob_regex_cache_mutex;
182
static std::list<std::string> g_glob_regex_cache_lru;
183
static std::unordered_map<std::string, GlobRegexCacheEntry> g_glob_regex_cache;
184
185
286
std::shared_ptr<RE2> get_or_build_re2(const std::string& glob_pattern) {
186
286
    {
187
286
        std::lock_guard<std::mutex> lock(g_glob_regex_cache_mutex);
188
286
        auto it = g_glob_regex_cache.find(glob_pattern);
189
286
        if (it != g_glob_regex_cache.end()) {
190
230
            g_glob_regex_cache_lru.splice(g_glob_regex_cache_lru.begin(), g_glob_regex_cache_lru,
191
230
                                          it->second.lru_it);
192
230
            return it->second.re2;
193
230
        }
194
286
    }
195
56
    std::string regex_pattern;
196
56
    Status st = glob_to_regex(glob_pattern, &regex_pattern);
197
56
    if (!st.ok()) {
198
2
        return nullptr;
199
2
    }
200
54
    auto compiled = std::make_shared<RE2>(regex_pattern);
201
54
    if (!compiled->ok()) {
202
3
        return nullptr;
203
3
    }
204
51
    {
205
51
        std::lock_guard<std::mutex> lock(g_glob_regex_cache_mutex);
206
51
        auto it = g_glob_regex_cache.find(glob_pattern);
207
51
        if (it != g_glob_regex_cache.end()) {
208
0
            g_glob_regex_cache_lru.splice(g_glob_regex_cache_lru.begin(), g_glob_regex_cache_lru,
209
0
                                          it->second.lru_it);
210
0
            return it->second.re2;
211
0
        }
212
51
        g_glob_regex_cache_lru.push_front(glob_pattern);
213
51
        g_glob_regex_cache.emplace(glob_pattern,
214
51
                                   GlobRegexCacheEntry {compiled, g_glob_regex_cache_lru.begin()});
215
51
        if (g_glob_regex_cache.size() > kGlobRegexCacheCapacity) {
216
0
            const std::string& evict_key = g_glob_regex_cache_lru.back();
217
0
            g_glob_regex_cache.erase(evict_key);
218
0
            g_glob_regex_cache_lru.pop_back();
219
0
        }
220
51
    }
221
0
    return compiled;
222
51
}
223
224
// Convert a restricted glob pattern into a regex.
225
// Supported: '*', '?', '[...]', '\\' escape. Others are treated as literals.
226
94
Status glob_to_regex(const std::string& glob_pattern, std::string* regex_pattern) {
227
94
    regex_pattern->clear();
228
94
    regex_pattern->append("^");
229
94
    bool is_escaped = false;
230
94
    size_t pattern_length = glob_pattern.size();
231
447
    for (size_t index = 0; index < pattern_length; ++index) {
232
357
        char current_char = glob_pattern[index];
233
357
        if (is_escaped) {
234
9
            append_escaped_regex_char(regex_pattern, current_char);
235
9
            is_escaped = false;
236
9
            continue;
237
9
        }
238
348
        if (current_char == '\\') {
239
13
            is_escaped = true;
240
13
            continue;
241
13
        }
242
335
        if (current_char == '*') {
243
24
            regex_pattern->append(".*");
244
24
            continue;
245
24
        }
246
311
        if (current_char == '?') {
247
13
            regex_pattern->append(".");
248
13
            continue;
249
13
        }
250
298
        if (current_char == '[') {
251
32
            size_t class_index = index + 1;
252
32
            bool class_closed = false;
253
32
            bool is_class_escaped = false;
254
32
            std::string class_buffer;
255
32
            if (class_index < pattern_length &&
256
32
                (glob_pattern[class_index] == '!' || glob_pattern[class_index] == '^')) {
257
9
                class_buffer.push_back('^');
258
9
                ++class_index;
259
9
            }
260
95
            for (; class_index < pattern_length; ++class_index) {
261
91
                char class_char = glob_pattern[class_index];
262
91
                if (is_class_escaped) {
263
10
                    class_buffer.push_back(class_char);
264
10
                    is_class_escaped = false;
265
10
                    continue;
266
10
                }
267
81
                if (class_char == '\\') {
268
10
                    is_class_escaped = true;
269
10
                    continue;
270
10
                }
271
71
                if (class_char == ']') {
272
28
                    class_closed = true;
273
28
                    break;
274
28
                }
275
43
                class_buffer.push_back(class_char);
276
43
            }
277
32
            if (!class_closed) {
278
4
                return Status::InvalidArgument("Unclosed character class in glob pattern: {}",
279
4
                                               glob_pattern);
280
4
            }
281
28
            regex_pattern->append("[");
282
28
            regex_pattern->append(class_buffer);
283
28
            regex_pattern->append("]");
284
28
            index = class_index;
285
28
            continue;
286
32
        }
287
266
        append_escaped_regex_char(regex_pattern, current_char);
288
266
    }
289
90
    if (is_escaped) {
290
4
        append_escaped_regex_char(regex_pattern, '\\');
291
4
    }
292
90
    regex_pattern->append("$");
293
90
    return Status::OK();
294
94
}
295
296
286
bool glob_match_re2(const std::string& glob_pattern, const std::string& candidate_path) {
297
286
    auto compiled = get_or_build_re2(glob_pattern);
298
286
    if (compiled == nullptr) {
299
5
        return false;
300
5
    }
301
281
    return RE2::FullMatch(candidate_path, *compiled);
302
286
}
303
304
// NestedGroup's physical children and offsets are produced by NestedGroupWriteProvider, not by
305
// appending TabletSchema extracted columns here. This predicate keeps only ordinary Variant paths
306
// that are outside the NG tree, for example `v.owner` beside `v.items[*]`.
307
0
bool is_regular_path_outside_nested_group(const PathInData& path) {
308
0
    const std::string& relative_path = path.get_path();
309
0
    return !relative_path.empty() && !path.get_is_typed() && !path.has_nested_part() &&
310
0
           !segment_v2::contains_nested_group_marker(relative_path) &&
311
0
           !segment_v2::is_root_nested_group_path(relative_path) &&
312
0
           relative_path != SPARSE_COLUMN_PATH &&
313
0
           relative_path.find(DOC_VALUE_COLUMN_PATH) == std::string::npos;
314
0
}
315
316
bool should_materialize_nested_group_regular_subcolumns(
317
        const TabletColumnPtr& column,
318
39
        const std::unordered_map<int32_t, VariantExtendedInfo>& uid_to_variant_extended_info) {
319
39
    const auto info_it = uid_to_variant_extended_info.find(column->unique_id());
320
39
    return column->variant_enable_nested_group() ||
321
39
           (info_it != uid_to_variant_extended_info.end() && info_it->second.has_nested_group);
322
39
}
323
324
std::unordered_set<int32_t> collect_nested_group_compaction_root_uids(
325
        const TabletSchemaSPtr& target,
326
72
        const std::unordered_map<int32_t, VariantExtendedInfo>& uid_to_variant_extended_info) {
327
72
    std::unordered_set<int32_t> root_uids;
328
891
    for (const TabletColumnPtr& column : target->columns()) {
329
891
        if (column->is_variant_type() && should_materialize_nested_group_regular_subcolumns(
330
39
                                                 column, uid_to_variant_extended_info)) {
331
1
            root_uids.insert(column->unique_id());
332
1
        }
333
891
    }
334
72
    return root_uids;
335
72
}
336
337
PathToDataTypes collect_regular_types_outside_nested_group(
338
1
        const VariantExtendedInfo& extended_info) {
339
1
    PathToDataTypes regular_path_to_data_types;
340
1
    for (const auto& [path, data_types] : extended_info.path_to_data_types) {
341
0
        if (!is_regular_path_outside_nested_group(path)) {
342
0
            continue;
343
0
        }
344
0
        regular_path_to_data_types.emplace(path, data_types);
345
0
    }
346
1
    return regular_path_to_data_types;
347
1
}
348
349
80
size_t get_number_of_dimensions(const IDataType& type) {
350
80
    if (const auto* type_array = typeid_cast<const DataTypeArray*>(&type)) {
351
4
        return type_array->get_number_of_dimensions();
352
4
    }
353
76
    return 0;
354
80
}
355
3
size_t get_number_of_dimensions(const IColumn& column) {
356
3
    if (const auto* column_array = check_and_get_column<ColumnArray>(column)) {
357
2
        return column_array->get_number_of_dimensions();
358
2
    }
359
1
    return 0;
360
3
}
361
362
1.44k
DataTypePtr get_base_type_of_array(const DataTypePtr& type) {
363
    /// Get raw pointers to avoid extra copying of type pointers.
364
1.44k
    const DataTypeArray* last_array = nullptr;
365
1.44k
    const auto* current_type = type.get();
366
1.44k
    if (const auto* nullable = typeid_cast<const DataTypeNullable*>(current_type)) {
367
1.43k
        current_type = nullable->get_nested_type().get();
368
1.43k
    }
369
1.53k
    while (const auto* type_array = typeid_cast<const DataTypeArray*>(current_type)) {
370
88
        current_type = type_array->get_nested_type().get();
371
88
        last_array = type_array;
372
88
        if (const auto* nullable = typeid_cast<const DataTypeNullable*>(current_type)) {
373
88
            current_type = nullable->get_nested_type().get();
374
88
        }
375
88
    }
376
1.44k
    return last_array ? last_array->get_nested_type() : type;
377
1.44k
}
378
379
50.1k
Status cast_column(const ColumnWithTypeAndName& arg, const DataTypePtr& type, ColumnPtr* result) {
380
50.1k
    ColumnsWithTypeAndName arguments {arg, {nullptr, type, type->get_name()}};
381
382
    // To prevent from null info lost, we should not call function since the function framework will wrap
383
    // nullable to Variant instead of the root of Variant
384
    // correct output: Nullable(Array(int)) -> Nullable(Variant(Nullable(Array(int))))
385
    // incorrect output: Nullable(Array(int)) -> Nullable(Variant(Array(int)))
386
    // ColumnVariantV2 owns its encoded/typed representation and its CAST implementation preserves
387
    // the outer null map. The manual root-wrapping path below is V1-only and would rebuild V2 as a
388
    // legacy ColumnVariant.
389
50.1k
    const bool target_is_variant_v2 =
390
50.1k
            dynamic_cast<const DataTypeVariantV2*>(remove_nullable(type).get()) != nullptr;
391
50.1k
    if (type->get_primitive_type() == TYPE_VARIANT && !target_is_variant_v2) {
392
        // If source column is variant, so the nullable info is different from dst column
393
46
        if (arg.type->get_primitive_type() == TYPE_VARIANT) {
394
9
            *result = type->is_nullable() ? make_nullable(arg.column) : remove_nullable(arg.column);
395
9
            return Status::OK();
396
9
        }
397
        // set variant root column/type to from column/type
398
46
        CHECK(is_column_nullable(*arg.column));
399
37
        auto to_type = remove_nullable(type);
400
37
        const auto& data_type_object = assert_cast<const DataTypeVariant&>(*to_type);
401
37
        auto variant = ColumnVariant::create(data_type_object.variant_max_subcolumns_count(),
402
37
                                             data_type_object.enable_doc_mode());
403
404
37
        variant->create_root(arg.type, IColumn::mutate(arg.column));
405
37
        ColumnPtr nullable = ColumnNullable::create(
406
37
                variant->get_ptr(),
407
37
                assert_cast<const ColumnNullable*>(arg.column.get())->get_null_map_column_ptr());
408
37
        *result = type->is_nullable() ? nullable : variant->get_ptr();
409
37
        return Status::OK();
410
46
    }
411
412
50.0k
    auto function = SimpleFunctionFactory::instance().get_function("CAST", arguments, type);
413
50.0k
    if (!function) {
414
0
        return Status::InternalError("Not found cast function {} to {}", arg.type->get_name(),
415
0
                                     type->get_name());
416
0
    }
417
50.0k
    Block tmp_block {arguments};
418
50.0k
    uint32_t result_column = cast_set<uint32_t>(tmp_block.columns());
419
50.0k
    RuntimeState state;
420
50.0k
    auto ctx = FunctionContext::create_context(&state, {}, {});
421
422
50.0k
    if (arg.type->get_primitive_type() == INVALID_TYPE) {
423
        // cast from nothing to any type should result in nulls
424
831
        *result = type->create_column_const_with_default_value(arg.column->size())
425
831
                          ->convert_to_full_column_if_const();
426
831
        return Status::OK();
427
831
    }
428
429
    // We convert column string to jsonb type just add a string jsonb field to dst column instead of parse
430
    // each line in original string column.
431
49.2k
    ctx->set_string_as_jsonb_string(true);
432
49.2k
    ctx->set_jsonb_string_as_string(true);
433
49.2k
    tmp_block.insert({nullptr, type, arg.name});
434
    // TODO(lihangyu): we should handle this error in strict mode
435
49.2k
    Status cast_status =
436
49.2k
            function->execute(ctx.get(), tmp_block, {0}, result_column, arg.column->size());
437
49.2k
    if (!cast_status.ok()) {
438
        // Variant V2 deliberately rejects source types without a physical encoding (currently
439
        // Decimal256). Publishing the legacy all-null fallback would silently lose stored values.
440
1
        if (target_is_variant_v2) {
441
1
            return cast_status;
442
1
        }
443
1
        LOG_EVERY_N(WARNING, 100) << fmt::format("cast from {} to {}", arg.type->get_name(),
444
0
                                                 type->get_name());
445
0
        *result = type->create_column_const_with_default_value(arg.column->size())
446
0
                          ->convert_to_full_column_if_const();
447
0
        return Status::OK();
448
1
    }
449
49.2k
    *result = tmp_block.get_by_position(result_column).column->convert_to_full_column_if_const();
450
49.2k
    VLOG_DEBUG << fmt::format("{} before convert {}, after convert {}", arg.name,
451
0
                              arg.column->get_name(), (*result)->get_name());
452
49.2k
    return Status::OK();
453
49.2k
}
454
455
10
ColumnPtr jsonb_root_to_json_string_column(const IColumn& root) {
456
10
    auto root_column = root.convert_to_full_column_if_const();
457
10
    const IColumn* jsonb_column = root_column.get();
458
10
    const NullMap* null_map = nullptr;
459
10
    if (root_column->is_nullable()) {
460
8
        const auto& nullable = assert_cast<const ColumnNullable&>(*root_column);
461
8
        jsonb_column = &nullable.get_nested_column();
462
8
        null_map = &nullable.get_null_map_data();
463
8
    }
464
465
10
    const auto& column = assert_cast<const ColumnString&>(*jsonb_column);
466
10
    auto result = ColumnString::create();
467
10
    result->reserve(column.size());
468
36
    for (size_t i = 0; i < column.size(); ++i) {
469
26
        if (null_map != nullptr && (*null_map)[i]) {
470
8
            result->insert_default();
471
8
            continue;
472
8
        }
473
474
18
        const auto jsonb = column.get_data_at(i);
475
18
        if (jsonb.size == 0) {
476
0
            result->insert_default();
477
0
            continue;
478
0
        }
479
480
18
        const auto json = JsonbToJson::jsonb_to_json_string(jsonb.data, jsonb.size);
481
18
        result->insert_data(json.data(), json.size());
482
18
    }
483
10
    return result->get_ptr();
484
10
}
485
486
void get_column_by_type(const DataTypePtr& data_type, const std::string& name, TabletColumn& column,
487
3.33k
                        const ExtraInfo& ext_info) {
488
3.33k
    column.set_name(name);
489
3.33k
    column.set_type(data_type->get_storage_field_type());
490
3.33k
    if (ext_info.unique_id >= 0) {
491
6
        column.set_unique_id(ext_info.unique_id);
492
6
    }
493
3.33k
    if (ext_info.parent_unique_id >= 0) {
494
1.59k
        column.set_parent_unique_id(ext_info.parent_unique_id);
495
1.59k
    }
496
3.33k
    if (!ext_info.path_info.empty()) {
497
1.59k
        column.set_path_info(ext_info.path_info);
498
1.59k
    }
499
3.33k
    if (data_type->is_nullable()) {
500
1.65k
        const auto& real_type = static_cast<const DataTypeNullable&>(*data_type);
501
1.65k
        column.set_is_nullable(true);
502
1.65k
        get_column_by_type(real_type.get_nested_type(), name, column, {});
503
1.65k
        return;
504
1.65k
    }
505
1.67k
    if (data_type->get_primitive_type() == PrimitiveType::TYPE_ARRAY) {
506
80
        TabletColumn child;
507
80
        get_column_by_type(assert_cast<const DataTypeArray*>(data_type.get())->get_nested_type(),
508
80
                           "", child, {});
509
80
        column.set_length(TabletColumn::get_field_length_by_type(TPrimitiveType::ARRAY, 0));
510
80
        column.add_sub_column(child);
511
80
        return;
512
80
    }
513
1.59k
    if (data_type->get_primitive_type() == PrimitiveType::TYPE_VARIANT) {
514
25
        const auto* variant_v1 = typeid_cast<const DataTypeVariant*>(data_type.get());
515
25
        const auto* variant_v2 = typeid_cast<const DataTypeVariantV2*>(data_type.get());
516
25
        DORIS_CHECK(variant_v1 != nullptr || variant_v2 != nullptr);
517
25
        column.set_variant_max_subcolumns_count(
518
25
                variant_v2 != nullptr ? variant_v2->variant_max_subcolumns_count()
519
25
                                      : variant_v1->variant_max_subcolumns_count());
520
25
        column.set_variant_enable_doc_mode(variant_v2 != nullptr ? variant_v2->enable_doc_mode()
521
25
                                                                 : variant_v1->enable_doc_mode());
522
25
        column.set_variant_is_v2(variant_v2 != nullptr);
523
25
        return;
524
25
    }
525
    // size is not fixed when type is string or json
526
1.57k
    if (is_string_type(data_type->get_primitive_type()) ||
527
1.57k
        data_type->get_primitive_type() == TYPE_JSONB) {
528
571
        column.set_length(INT_MAX);
529
571
        return;
530
571
    }
531
532
1.00k
    PrimitiveType type = data_type->get_primitive_type();
533
1.00k
    if (is_int_or_bool(type) || is_string_type(type) || is_float_or_double(type) || is_ip(type) ||
534
1.00k
        is_date_or_datetime(type) || type == PrimitiveType::TYPE_DATEV2) {
535
998
        column.set_length(cast_set<int32_t>(data_type->get_size_of_value_in_memory()));
536
998
        return;
537
998
    }
538
5
    if (is_decimal(type)) {
539
1
        column.set_precision(data_type->get_precision());
540
1
        column.set_frac(data_type->get_scale());
541
1
        return;
542
1
    }
543
    // datetimev2 needs scale
544
4
    if (type == PrimitiveType::TYPE_DATETIMEV2 || type == PrimitiveType::TYPE_TIMESTAMPTZ) {
545
3
        column.set_precision(-1);
546
3
        column.set_frac(data_type->get_scale());
547
3
        return;
548
3
    }
549
550
1
    throw doris::Exception(doris::ErrorCode::INTERNAL_ERROR,
551
1
                           "unexcepted data column type: {}, column name is: {}",
552
1
                           data_type->get_name(), name);
553
4
}
554
555
TabletColumn get_column_by_type(const DataTypePtr& data_type, const std::string& name,
556
1.56k
                                const ExtraInfo& ext_info) {
557
1.56k
    TabletColumn result;
558
1.56k
    get_column_by_type(data_type, name, result, ext_info);
559
1.56k
    return result;
560
1.56k
}
561
562
// check if two paths which same prefix have different structure
563
static bool has_different_structure_in_same_path(const PathInData::Parts& lhs,
564
9.01k
                                                 const PathInData::Parts& rhs) {
565
9.01k
    if (lhs.size() != rhs.size()) {
566
1
        return false; // different size means different structure
567
1
    }
568
    // Since we group by path string, lhs and rhs must have the same size and keys
569
    // We only need to check if they have different nested structure
570
36.0k
    for (size_t i = 0; i < lhs.size(); ++i) {
571
27.0k
        if (lhs[i] != rhs[i]) {
572
5
            VLOG_DEBUG << fmt::format(
573
0
                    "Check different structure: {} vs {}, lhs[i].is_nested: {}, rhs[i].is_nested: "
574
0
                    "{}",
575
0
                    lhs[i].key, rhs[i].key, lhs[i].is_nested, rhs[i].is_nested);
576
5
            return true;
577
5
        }
578
27.0k
    }
579
9.01k
    return false;
580
9.01k
}
581
582
3.05k
Status check_variant_has_no_ambiguous_paths(const PathsInData& tuple_paths) {
583
    // Group paths by their string representation to reduce comparisons
584
3.05k
    std::unordered_map<std::string, std::vector<size_t>> path_groups;
585
586
24.1k
    for (size_t i = 0; i < tuple_paths.size(); ++i) {
587
        // same path should have same structure, so we group them by path
588
21.0k
        path_groups[tuple_paths[i].get_path()].push_back(i);
589
        // print part of tuple_paths[i]
590
21.0k
        VLOG_DEBUG << "tuple_paths[i]: " << tuple_paths[i].get_path();
591
21.0k
    }
592
593
    // Only compare paths within the same group
594
12.0k
    for (const auto& [path_str, indices] : path_groups) {
595
12.0k
        if (indices.size() <= 1) {
596
3.05k
            continue; // No conflicts possible
597
3.05k
        }
598
599
        // Compare all pairs within this group
600
27.0k
        for (size_t i = 0; i < indices.size(); ++i) {
601
27.0k
            for (size_t j = 0; j < i; ++j) {
602
9.01k
                if (has_different_structure_in_same_path(tuple_paths[indices[i]].get_parts(),
603
9.01k
                                                         tuple_paths[indices[j]].get_parts())) {
604
5
                    return Status::DataQualityError(
605
5
                            "Ambiguous paths: {} vs {} with different nested part {} vs {}",
606
5
                            tuple_paths[indices[i]].get_path(), tuple_paths[indices[j]].get_path(),
607
5
                            tuple_paths[indices[i]].has_nested_part(),
608
5
                            tuple_paths[indices[j]].has_nested_part());
609
5
                }
610
9.01k
            }
611
18.0k
        }
612
9.01k
    }
613
3.05k
    return Status::OK();
614
3.05k
}
615
616
Status update_least_schema_internal(const std::map<PathInData, DataTypes>& subcolumns_types,
617
                                    TabletSchemaSPtr& common_schema, int32_t variant_col_unique_id,
618
                                    const std::map<std::string, TabletColumnPtr>& typed_columns,
619
43
                                    std::set<PathInData>* path_set) {
620
43
    PathsInData tuple_paths;
621
43
    DataTypes tuple_types;
622
43
    CHECK(common_schema.use_count() == 1);
623
    // Get the least common type for all paths.
624
56
    for (const auto& [key, subtypes] : subcolumns_types) {
625
56
        assert(!subtypes.empty());
626
56
        if (key.get_path() == ColumnVariant::COLUMN_NAME_DUMMY) {
627
0
            continue;
628
0
        }
629
56
        size_t first_dim = get_number_of_dimensions(*subtypes[0]);
630
56
        tuple_paths.emplace_back(key);
631
76
        for (size_t i = 1; i < subtypes.size(); ++i) {
632
21
            if (first_dim != get_number_of_dimensions(*subtypes[i])) {
633
1
                tuple_types.emplace_back(make_nullable(std::make_shared<DataTypeJsonb>()));
634
1
                LOG(INFO) << fmt::format(
635
1
                        "Uncompatible types of subcolumn '{}': {} and {}, cast to JSONB",
636
1
                        key.get_path(), subtypes[0]->get_name(), subtypes[i]->get_name());
637
1
                break;
638
1
            }
639
21
        }
640
56
        if (tuple_paths.size() == tuple_types.size()) {
641
1
            continue;
642
1
        }
643
55
        DataTypePtr common_type;
644
55
        get_least_supertype_jsonb(subtypes, &common_type);
645
55
        if (!common_type->is_nullable()) {
646
3
            common_type = make_nullable(common_type);
647
3
        }
648
55
        tuple_types.emplace_back(common_type);
649
55
    }
650
43
    CHECK_EQ(tuple_paths.size(), tuple_types.size());
651
652
    // Append all common type columns of this variant
653
99
    for (int i = 0; i < tuple_paths.size(); ++i) {
654
56
        TabletColumn common_column;
655
        // typed path not contains root part
656
56
        auto path_without_root = tuple_paths[i].copy_pop_front().get_path();
657
56
        if (typed_columns.contains(path_without_root) && !tuple_paths[i].has_nested_part()) {
658
17
            common_column = *typed_columns.at(path_without_root);
659
            // parent unique id and path may not be init in write path
660
17
            common_column.set_parent_unique_id(variant_col_unique_id);
661
17
            common_column.set_path_info(tuple_paths[i]);
662
17
            common_column.set_name(tuple_paths[i].get_path());
663
39
        } else {
664
            // const std::string& column_name = variant_col_name + "." + tuple_paths[i].get_path();
665
39
            get_column_by_type(tuple_types[i], tuple_paths[i].get_path(), common_column,
666
39
                               ExtraInfo {.unique_id = -1,
667
39
                                          .parent_unique_id = variant_col_unique_id,
668
39
                                          .path_info = tuple_paths[i]});
669
39
        }
670
56
        common_schema->append_column(common_column);
671
56
        if (path_set != nullptr) {
672
53
            path_set->insert(tuple_paths[i]);
673
53
        }
674
56
    }
675
43
    return Status::OK();
676
43
}
677
678
Status update_least_common_schema(const std::vector<TabletSchemaSPtr>& schemas,
679
                                  TabletSchemaSPtr& common_schema, int32_t variant_col_unique_id,
680
42
                                  std::set<PathInData>* path_set) {
681
42
    std::map<std::string, TabletColumnPtr> typed_columns;
682
42
    for (const TabletColumnPtr& col :
683
48
         common_schema->column_by_uid(variant_col_unique_id).get_sub_columns()) {
684
48
        typed_columns[col->name()] = col;
685
48
    }
686
    // Types of subcolumns by path from all tuples.
687
42
    std::map<PathInData, DataTypes> subcolumns_types;
688
689
    // Collect all paths first to enable batch checking
690
42
    std::vector<PathInData> all_paths;
691
692
60
    for (const TabletSchemaSPtr& schema : schemas) {
693
178
        for (const TabletColumnPtr& col : schema->columns()) {
694
            // Get subcolumns of this variant
695
178
            if (col->has_path_info() && col->parent_unique_id() >= 0 &&
696
178
                col->parent_unique_id() == variant_col_unique_id) {
697
72
                subcolumns_types[*col->path_info_ptr()].emplace_back(
698
72
                        DataTypeFactory::instance().create_data_type(*col, col->is_nullable()));
699
72
                all_paths.push_back(*col->path_info_ptr());
700
72
            }
701
178
        }
702
60
    }
703
704
    // Batch check for conflicts
705
42
    RETURN_IF_ERROR(check_variant_has_no_ambiguous_paths(all_paths));
706
707
42
    return update_least_schema_internal(subcolumns_types, common_schema, variant_col_unique_id,
708
42
                                        typed_columns, path_set);
709
42
}
710
711
// Keep variant subcolumn BF support aligned with FE DDL checks.
712
1.60k
bool is_bf_supported_by_fe_for_variant_subcolumn(FieldType type) {
713
1.60k
    switch (type) {
714
0
    case FieldType::OLAP_FIELD_TYPE_SMALLINT:
715
59
    case FieldType::OLAP_FIELD_TYPE_INT:
716
804
    case FieldType::OLAP_FIELD_TYPE_BIGINT:
717
804
    case FieldType::OLAP_FIELD_TYPE_LARGEINT:
718
804
    case FieldType::OLAP_FIELD_TYPE_CHAR:
719
804
    case FieldType::OLAP_FIELD_TYPE_VARCHAR:
720
1.37k
    case FieldType::OLAP_FIELD_TYPE_STRING:
721
1.37k
    case FieldType::OLAP_FIELD_TYPE_DATE:
722
1.37k
    case FieldType::OLAP_FIELD_TYPE_DATETIME:
723
1.38k
    case FieldType::OLAP_FIELD_TYPE_DATEV2:
724
1.38k
    case FieldType::OLAP_FIELD_TYPE_DATETIMEV2:
725
1.38k
    case FieldType::OLAP_FIELD_TYPE_TIMESTAMPTZ:
726
1.38k
    case FieldType::OLAP_FIELD_TYPE_DECIMAL:
727
1.38k
    case FieldType::OLAP_FIELD_TYPE_DECIMAL32:
728
1.38k
    case FieldType::OLAP_FIELD_TYPE_DECIMAL64:
729
1.38k
    case FieldType::OLAP_FIELD_TYPE_DECIMAL128I:
730
1.38k
    case FieldType::OLAP_FIELD_TYPE_DECIMAL256:
731
1.38k
    case FieldType::OLAP_FIELD_TYPE_IPV4:
732
1.38k
    case FieldType::OLAP_FIELD_TYPE_IPV6:
733
1.38k
        return true;
734
223
    default:
735
223
        return false;
736
1.60k
    }
737
1.60k
}
738
739
void inherit_column_attributes(const TabletColumn& source, TabletColumn& target,
740
1.60k
                               TabletSchemaSPtr* target_schema) {
741
1.60k
    if (!target.is_extracted_column()) {
742
0
        return;
743
0
    }
744
1.60k
    target.set_aggregation_method(source.aggregation());
745
746
    // 1. bloom filter
747
1.60k
    if (is_bf_supported_by_fe_for_variant_subcolumn(target.type())) {
748
1.38k
        target.set_is_bf_column(source.is_bf_column());
749
1.38k
    }
750
751
1.60k
    if (!target_schema) {
752
1.55k
        return;
753
1.55k
    }
754
755
    // 2. inverted index
756
57
    TabletIndexes indexes_to_add;
757
57
    auto source_indexes = (*target_schema)->inverted_indexs(source.unique_id());
758
    // if target is variant type, we need to inherit all indexes
759
    // because this schema is a read schema from fe
760
57
    if (target.is_variant_type()) {
761
0
        for (auto& index : source_indexes) {
762
0
            auto index_info = std::make_shared<TabletIndex>(*index);
763
0
            index_info->set_escaped_escaped_index_suffix_path(target.path_info_ptr()->get_path());
764
0
            indexes_to_add.emplace_back(std::move(index_info));
765
0
        }
766
57
    } else {
767
57
        inherit_index(source_indexes, indexes_to_add, target);
768
57
    }
769
57
    auto target_indexes = (*target_schema)
770
57
                                  ->inverted_indexs(target.parent_unique_id(),
771
57
                                                    target.path_info_ptr()->get_path());
772
57
    if (target_indexes.empty()) {
773
57
        for (auto& index_info : indexes_to_add) {
774
14
            (*target_schema)->append_index(std::move(*index_info));
775
14
        }
776
57
    }
777
778
    // 3. TODO: gnragm bf index
779
57
}
780
781
41
void inherit_column_attributes(TabletSchemaSPtr& schema) {
782
    // Add index meta if extracted column is missing index meta
783
177
    for (size_t i = 0; i < schema->num_columns(); ++i) {
784
136
        TabletColumn& col = schema->mutable_column(i);
785
136
        if (!col.is_extracted_column()) {
786
79
            continue;
787
79
        }
788
57
        if (schema->field_index(col.parent_unique_id()) == -1) {
789
            // parent column is missing, maybe dropped
790
0
            continue;
791
0
        }
792
57
        inherit_column_attributes(schema->column_by_uid(col.parent_unique_id()), col, &schema);
793
57
    }
794
41
}
795
796
Status get_least_common_schema(const std::vector<TabletSchemaSPtr>& schemas,
797
                               const TabletSchemaSPtr& base_schema, TabletSchemaSPtr& output_schema,
798
39
                               bool check_schema_size) {
799
39
    std::vector<int32_t> variant_column_unique_id;
800
    // Construct a schema excluding the extracted columns and gather unique identifiers for variants.
801
    // Ensure that the output schema also excludes these extracted columns. This approach prevents
802
    // duplicated paths following the update_least_common_schema process.
803
39
    auto build_schema_without_extracted_columns = [&](const TabletSchemaSPtr& base_schema) {
804
39
        output_schema = std::make_shared<TabletSchema>();
805
        // not copy columns but only shadow copy other attributes
806
39
        output_schema->shawdow_copy_without_columns(*base_schema);
807
        // Get all columns without extracted columns and collect variant col unique id
808
77
        for (const TabletColumnPtr& col : base_schema->columns()) {
809
77
            if (col->is_variant_type()) {
810
39
                variant_column_unique_id.push_back(col->unique_id());
811
39
            }
812
77
            if (!col->is_extracted_column()) {
813
74
                output_schema->append_column(*col);
814
74
            }
815
77
        }
816
39
    };
817
39
    if (base_schema == nullptr) {
818
        // Pick tablet schema with max schema version
819
4
        auto max_version_schema =
820
4
                *std::max_element(schemas.cbegin(), schemas.cend(),
821
4
                                  [](const TabletSchemaSPtr a, const TabletSchemaSPtr b) {
822
2
                                      return a->schema_version() < b->schema_version();
823
2
                                  });
824
4
        CHECK(max_version_schema);
825
4
        build_schema_without_extracted_columns(max_version_schema);
826
35
    } else {
827
        // use input base_schema schema as base schema
828
35
        build_schema_without_extracted_columns(base_schema);
829
35
    }
830
831
39
    for (int32_t unique_id : variant_column_unique_id) {
832
39
        std::set<PathInData> path_set;
833
39
        RETURN_IF_ERROR(update_least_common_schema(schemas, output_schema, unique_id, &path_set));
834
39
    }
835
836
39
    inherit_column_attributes(output_schema);
837
39
    if (check_schema_size &&
838
39
        output_schema->columns().size() > config::variant_max_merged_tablet_schema_size) {
839
0
        return Status::DataQualityError("Reached max column size limit {}",
840
0
                                        config::variant_max_merged_tablet_schema_size);
841
0
    }
842
843
39
    return Status::OK();
844
39
}
845
846
// sort by paths in lexicographical order
847
813
ColumnVariant::Subcolumns get_sorted_subcolumns(const ColumnVariant::Subcolumns& subcolumns) {
848
    // sort by paths in lexicographical order
849
813
    ColumnVariant::Subcolumns sorted = subcolumns;
850
23.6k
    std::sort(sorted.begin(), sorted.end(), [](const auto& lhsItem, const auto& rhsItem) {
851
23.6k
        return lhsItem->path < rhsItem->path;
852
23.6k
    });
853
813
    return sorted;
854
813
}
855
856
bool has_schema_index_diff(const TabletSchema* new_schema, const TabletSchema* old_schema,
857
4
                           int32_t new_col_idx, int32_t old_col_idx) {
858
4
    const auto& column_new = new_schema->column(new_col_idx);
859
4
    const auto& column_old = old_schema->column(old_col_idx);
860
861
4
    if (column_new.is_bf_column() != column_old.is_bf_column()) {
862
2
        return true;
863
2
    }
864
865
2
    auto new_schema_inverted_indexs = new_schema->inverted_indexs(column_new);
866
2
    auto old_schema_inverted_indexs = old_schema->inverted_indexs(column_old);
867
868
2
    if (new_schema_inverted_indexs.size() != old_schema_inverted_indexs.size()) {
869
1
        return true;
870
1
    }
871
872
2
    for (size_t i = 0; i < new_schema_inverted_indexs.size(); ++i) {
873
1
        if (!new_schema_inverted_indexs[i]->is_same_except_id(old_schema_inverted_indexs[i])) {
874
0
            return true;
875
0
        }
876
1
    }
877
878
1
    return false;
879
1
}
880
881
1.00k
TabletColumn create_sparse_column(const TabletColumn& variant) {
882
1.00k
    TabletColumn res;
883
1.00k
    res.set_name(variant.name_lower_case() + "." + SPARSE_COLUMN_PATH);
884
1.00k
    res.set_type(FieldType::OLAP_FIELD_TYPE_MAP);
885
1.00k
    res.set_aggregation_method(variant.aggregation());
886
1.00k
    res.set_path_info(PathInData {variant.name_lower_case() + "." + SPARSE_COLUMN_PATH});
887
1.00k
    res.set_parent_unique_id(variant.unique_id());
888
    // set default value to "NULL" DefaultColumnIterator will call insert_many_defaults
889
1.00k
    res.set_default_value("NULL");
890
1.00k
    TabletColumn child_tcolumn;
891
1.00k
    child_tcolumn.set_type(FieldType::OLAP_FIELD_TYPE_STRING);
892
1.00k
    res.add_sub_column(child_tcolumn);
893
1.00k
    res.add_sub_column(child_tcolumn);
894
1.00k
    return res;
895
1.00k
}
896
897
78
TabletColumn create_sparse_shard_column(const TabletColumn& variant, int bucket_index) {
898
78
    TabletColumn res;
899
78
    std::string name = variant.name_lower_case() + "." + SPARSE_COLUMN_PATH + ".b" +
900
78
                       std::to_string(bucket_index);
901
78
    res.set_name(name);
902
78
    res.set_type(FieldType::OLAP_FIELD_TYPE_MAP);
903
78
    res.set_aggregation_method(variant.aggregation());
904
78
    res.set_parent_unique_id(variant.unique_id());
905
78
    res.set_default_value("NULL");
906
78
    PathInData path(name);
907
78
    res.set_path_info(path);
908
78
    TabletColumn child_tcolumn;
909
78
    child_tcolumn.set_type(FieldType::OLAP_FIELD_TYPE_STRING);
910
78
    res.add_sub_column(child_tcolumn);
911
78
    res.add_sub_column(child_tcolumn);
912
78
    return res;
913
78
}
914
915
191
TabletColumn create_doc_value_column(const TabletColumn& variant, int bucket_index) {
916
191
    TabletColumn res;
917
191
    std::string name = variant.name_lower_case() + "." + DOC_VALUE_COLUMN_PATH + ".b" +
918
191
                       std::to_string(bucket_index);
919
191
    res.set_name(name);
920
191
    res.set_type(FieldType::OLAP_FIELD_TYPE_MAP);
921
191
    res.set_aggregation_method(variant.aggregation());
922
191
    res.set_parent_unique_id(variant.unique_id());
923
191
    res.set_default_value("NULL");
924
191
    res.set_path_info(PathInData {name});
925
926
191
    TabletColumn child_tcolumn;
927
191
    child_tcolumn.set_type(FieldType::OLAP_FIELD_TYPE_STRING);
928
191
    res.add_sub_column(child_tcolumn);
929
191
    res.add_sub_column(child_tcolumn);
930
191
    return res;
931
191
}
932
933
6.92k
uint32_t variant_binary_shard_of(const StringRef& path, uint32_t bucket_num) {
934
6.92k
    if (bucket_num <= 1) return 0;
935
6.52k
    SipHash hash;
936
6.52k
    hash.update(path.data, path.size);
937
6.52k
    uint64_t h = hash.get64();
938
6.52k
    return static_cast<uint32_t>(h % bucket_num);
939
6.92k
}
940
941
Status VariantCompactionUtil::aggregate_path_to_stats(
942
        const RowsetSharedPtr& rs,
943
112
        std::unordered_map<int32_t, PathToNoneNullValues>* uid_to_path_stats) {
944
112
    SegmentCacheHandle segment_cache;
945
112
    RETURN_IF_ERROR(SegmentLoader::instance()->load_segments(
946
112
            std::static_pointer_cast<BetaRowset>(rs), &segment_cache));
947
948
359
    for (const auto& column : rs->tablet_schema()->columns()) {
949
359
        if (!column->is_variant_type() || column->unique_id() < 0) {
950
222
            continue;
951
222
        }
952
137
        if (!should_check_variant_path_stats(*column)) {
953
0
            continue;
954
0
        }
955
282
        for (const auto& segment : segment_cache.get_segments()) {
956
282
            std::shared_ptr<ColumnReader> column_reader;
957
282
            OlapReaderStatistics stats;
958
282
            RETURN_IF_ERROR(
959
282
                    segment->get_column_reader(column->unique_id(), &column_reader, &stats));
960
282
            if (!column_reader) {
961
0
                continue;
962
0
            }
963
964
282
            CHECK(column_reader->get_meta_type() == FieldType::OLAP_FIELD_TYPE_VARIANT);
965
282
            auto* variant_column_reader =
966
282
                    assert_cast<segment_v2::VariantColumnReader*>(column_reader.get());
967
            // load external meta before getting stats
968
282
            RETURN_IF_ERROR(variant_column_reader->load_external_meta_once());
969
282
            const auto* source_stats = variant_column_reader->get_stats();
970
282
            CHECK(source_stats);
971
972
            // agg path -> stats
973
1.34k
            for (const auto& [path, size] : source_stats->sparse_column_non_null_size) {
974
1.34k
                (*uid_to_path_stats)[column->unique_id()][path] += size;
975
1.34k
            }
976
977
606
            for (const auto& [path, size] : source_stats->subcolumns_non_null_size) {
978
606
                (*uid_to_path_stats)[column->unique_id()][path] += size;
979
606
            }
980
282
        }
981
137
    }
982
112
    return Status::OK();
983
112
}
984
985
Status VariantCompactionUtil::aggregate_variant_extended_info(
986
        const RowsetSharedPtr& rs,
987
74
        std::unordered_map<int32_t, VariantExtendedInfo>* uid_to_variant_extended_info) {
988
74
    SegmentCacheHandle segment_cache;
989
74
    RETURN_IF_ERROR(SegmentLoader::instance()->load_segments(
990
74
            std::static_pointer_cast<BetaRowset>(rs), &segment_cache));
991
992
170
    for (const auto& column : rs->tablet_schema()->columns()) {
993
170
        if (!column->is_variant_type()) {
994
82
            continue;
995
82
        }
996
88
        auto& extended_info = (*uid_to_variant_extended_info)[column->unique_id()];
997
88
        if (column->variant_enable_nested_group()) {
998
0
            extended_info.has_nested_group = true;
999
0
        }
1000
135
        for (const auto& segment : segment_cache.get_segments()) {
1001
135
            std::shared_ptr<ColumnReader> column_reader;
1002
135
            OlapReaderStatistics stats;
1003
135
            RETURN_IF_ERROR(
1004
135
                    segment->get_column_reader(column->unique_id(), &column_reader, &stats));
1005
134
            if (!column_reader) {
1006
0
                continue;
1007
0
            }
1008
1009
134
            CHECK(column_reader->get_meta_type() == FieldType::OLAP_FIELD_TYPE_VARIANT);
1010
134
            auto* variant_column_reader =
1011
134
                    assert_cast<segment_v2::VariantColumnReader*>(column_reader.get());
1012
            // load external meta before getting stats
1013
134
            RETURN_IF_ERROR(variant_column_reader->load_external_meta_once());
1014
134
            const auto* source_stats = variant_column_reader->get_stats();
1015
134
            CHECK(source_stats);
1016
1017
134
            if (!column->variant_enable_nested_group()) {
1018
                // NG roots still need type metadata for regular subpaths such as `v.owner`,
1019
                // but their compaction schema should not be driven by flat path stats.
1020
487
                for (const auto& [path, size] : source_stats->sparse_column_non_null_size) {
1021
487
                    extended_info.path_to_none_null_values[path] += size;
1022
487
                    extended_info.sparse_paths.emplace(path);
1023
487
                }
1024
1025
246
                for (const auto& [path, size] : source_stats->subcolumns_non_null_size) {
1026
246
                    extended_info.path_to_none_null_values[path] += size;
1027
246
                }
1028
134
            }
1029
1030
            //2. agg path -> schema
1031
134
            variant_column_reader->get_subcolumns_types(&extended_info.path_to_data_types);
1032
1033
            // 3. extract typed paths
1034
134
            variant_column_reader->get_typed_paths(&extended_info.typed_paths);
1035
1036
            // 4. extract nested paths
1037
134
            if (!column->variant_enable_nested_group()) {
1038
134
                variant_column_reader->get_nested_paths(&extended_info.nested_paths);
1039
134
            }
1040
134
        }
1041
88
    }
1042
73
    return Status::OK();
1043
74
}
1044
1045
// get the subpaths and sparse paths for the variant column
1046
void VariantCompactionUtil::get_subpaths(int32_t max_subcolumns_count,
1047
                                         const PathToNoneNullValues& stats,
1048
51
                                         TabletSchema::PathsSetInfo& paths_set_info) {
1049
    // max_subcolumns_count is 0 means no limit
1050
51
    if (max_subcolumns_count > 0 && stats.size() > max_subcolumns_count) {
1051
23
        std::vector<std::pair<size_t, std::string_view>> paths_with_sizes;
1052
23
        paths_with_sizes.reserve(stats.size());
1053
121
        for (const auto& [path, size] : stats) {
1054
121
            paths_with_sizes.emplace_back(size, path);
1055
121
        }
1056
23
        std::sort(paths_with_sizes.begin(), paths_with_sizes.end(), std::greater());
1057
1058
        // Select top N paths as subcolumns, remaining paths as sparse columns
1059
121
        for (const auto& [size, path] : paths_with_sizes) {
1060
121
            if (paths_set_info.sub_path_set.size() < max_subcolumns_count) {
1061
37
                paths_set_info.sub_path_set.emplace(path);
1062
84
            } else {
1063
84
                paths_set_info.sparse_path_set.emplace(path);
1064
84
            }
1065
121
        }
1066
23
        LOG(INFO) << "subpaths " << paths_set_info.sub_path_set.size() << " sparse paths "
1067
23
                  << paths_set_info.sparse_path_set.size() << " variant max subcolumns count "
1068
23
                  << max_subcolumns_count << " stats size " << paths_with_sizes.size();
1069
28
    } else {
1070
        // Apply all paths as subcolumns
1071
45
        for (const auto& [path, _] : stats) {
1072
45
            paths_set_info.sub_path_set.emplace(path);
1073
45
        }
1074
28
    }
1075
51
}
1076
1077
Status VariantCompactionUtil::check_path_stats(const std::vector<RowsetSharedPtr>& intputs,
1078
46
                                               RowsetSharedPtr output, BaseTabletSPtr tablet) {
1079
46
    if (output->tablet_schema()->num_variant_columns() == 0) {
1080
9
        return Status::OK();
1081
9
    }
1082
87
    for (const auto& rowset : intputs) {
1083
256
        for (const auto& column : rowset->tablet_schema()->columns()) {
1084
256
            if (column->is_variant_type() && !should_check_variant_path_stats(*column)) {
1085
0
                return Status::OK();
1086
0
            }
1087
256
        }
1088
87
    }
1089
    // check no extended schema in input rowsets
1090
87
    for (const auto& rowset : intputs) {
1091
256
        for (const auto& column : rowset->tablet_schema()->columns()) {
1092
256
            if (column->is_extracted_column()) {
1093
0
                return Status::OK();
1094
0
            }
1095
256
        }
1096
87
    }
1097
#ifndef BE_TEST
1098
    // check no extended schema in output rowset
1099
    for (const auto& column : output->tablet_schema()->columns()) {
1100
        if (column->is_extracted_column()) {
1101
            const auto& name = column->name();
1102
            if (name.find("." + DOC_VALUE_COLUMN_PATH + ".") != std::string::npos ||
1103
                name.find("." + SPARSE_COLUMN_PATH + ".") != std::string::npos ||
1104
                name.ends_with("." + SPARSE_COLUMN_PATH)) {
1105
                continue;
1106
            }
1107
            return Status::InternalError("Unexpected extracted column {} in output rowset",
1108
                                         column->name());
1109
        }
1110
    }
1111
#endif
1112
    // only check path stats for dup_keys since the rows may be merged in other models
1113
37
    if (tablet->keys_type() != KeysType::DUP_KEYS) {
1114
2
        return Status::OK();
1115
2
    }
1116
    // if there is a delete predicate in the input rowsets, we skip the path stats check
1117
76
    for (auto& rowset : intputs) {
1118
76
        if (rowset->rowset_meta()->has_delete_predicate()) {
1119
0
            return Status::OK();
1120
0
        }
1121
76
    }
1122
153
    for (const auto& column : output->tablet_schema()->columns()) {
1123
153
        if (column->is_variant_type() && !should_check_variant_path_stats(*column)) {
1124
0
            return Status::OK();
1125
0
        }
1126
153
    }
1127
35
    std::unordered_map<int32_t, PathToNoneNullValues> original_uid_to_path_stats;
1128
76
    for (const auto& rs : intputs) {
1129
76
        RETURN_IF_ERROR(aggregate_path_to_stats(rs, &original_uid_to_path_stats));
1130
76
    }
1131
35
    std::unordered_map<int32_t, PathToNoneNullValues> output_uid_to_path_stats;
1132
35
    RETURN_IF_ERROR(aggregate_path_to_stats(output, &output_uid_to_path_stats));
1133
35
    for (const auto& [uid, stats] : output_uid_to_path_stats) {
1134
27
        if (output->tablet_schema()->column_by_uid(uid).is_variant_type() &&
1135
27
            output->tablet_schema()->column_by_uid(uid).variant_enable_doc_mode()) {
1136
0
            continue;
1137
0
        }
1138
27
        if (original_uid_to_path_stats.find(uid) == original_uid_to_path_stats.end()) {
1139
0
            return Status::InternalError("Path stats not found for uid {}, tablet_id {}", uid,
1140
0
                                         tablet->tablet_id());
1141
0
        }
1142
1143
        // In input rowsets, some rowsets may have statistics values exceeding the maximum limit,
1144
        // which leads to inaccurate statistics
1145
27
        if (stats.size() > output->tablet_schema()
1146
27
                                   ->column_by_uid(uid)
1147
27
                                   .variant_max_sparse_column_statistics_size()) {
1148
            // When there is only one segment, we can ensure that the size of each path in output stats is accurate
1149
8
            if (output->num_segments() == 1) {
1150
27
                for (const auto& [path, size] : stats) {
1151
27
                    if (original_uid_to_path_stats.at(uid).find(path) ==
1152
27
                        original_uid_to_path_stats.at(uid).end()) {
1153
2
                        continue;
1154
2
                    }
1155
25
                    if (original_uid_to_path_stats.at(uid).at(path) > size) {
1156
0
                        return Status::InternalError(
1157
0
                                "Path stats not smaller for uid {} with path `{}`, input size {}, "
1158
0
                                "output "
1159
0
                                "size {}, "
1160
0
                                "tablet_id {}",
1161
0
                                uid, path, original_uid_to_path_stats.at(uid).at(path), size,
1162
0
                                tablet->tablet_id());
1163
0
                    }
1164
25
                }
1165
8
            }
1166
8
        }
1167
        // in this case, input stats is accurate, so we check the stats size and stats value
1168
19
        else {
1169
81
            for (const auto& [path, size] : stats) {
1170
81
                if (original_uid_to_path_stats.at(uid).find(path) ==
1171
81
                    original_uid_to_path_stats.at(uid).end()) {
1172
0
                    return Status::InternalError(
1173
0
                            "Path stats not found for uid {}, path {}, tablet_id {}", uid, path,
1174
0
                            tablet->tablet_id());
1175
0
                }
1176
81
                if (original_uid_to_path_stats.at(uid).at(path) != size) {
1177
1
                    return Status::InternalError(
1178
1
                            "Path stats not match for uid {} with path `{}`, input size {}, output "
1179
1
                            "size {}, "
1180
1
                            "tablet_id {}",
1181
1
                            uid, path, original_uid_to_path_stats.at(uid).at(path), size,
1182
1
                            tablet->tablet_id());
1183
1
                }
1184
81
            }
1185
19
        }
1186
27
    }
1187
1188
34
    return Status::OK();
1189
35
}
1190
1191
Status VariantCompactionUtil::get_compaction_typed_columns(
1192
        const TabletSchemaSPtr& target, const std::unordered_set<std::string>& typed_paths,
1193
        const TabletColumnPtr parent_column, TabletSchemaSPtr& output_schema,
1194
40
        TabletSchema::PathsSetInfo& paths_set_info) {
1195
40
    if (parent_column->variant_enable_typed_paths_to_sparse()) {
1196
0
        return Status::OK();
1197
0
    }
1198
40
    for (const auto& path : typed_paths) {
1199
20
        TabletSchema::SubColumnInfo sub_column_info;
1200
20
        if (generate_sub_column_info(*target, parent_column->unique_id(), path, &sub_column_info)) {
1201
19
            inherit_column_attributes(*parent_column, sub_column_info.column);
1202
19
            output_schema->append_column(sub_column_info.column);
1203
19
            paths_set_info.typed_path_set.insert({path, std::move(sub_column_info)});
1204
19
            VLOG_DEBUG << "append typed column " << path;
1205
19
        } else {
1206
1
            return Status::InternalError("Failed to generate sub column info for path {}", path);
1207
1
        }
1208
20
    }
1209
39
    return Status::OK();
1210
40
}
1211
1212
Status VariantCompactionUtil::get_compaction_nested_columns(
1213
        const std::unordered_set<PathInData, PathInData::Hash>& nested_paths,
1214
        const PathToDataTypes& path_to_data_types, const TabletColumnPtr parent_column,
1215
39
        TabletSchemaSPtr& output_schema, TabletSchema::PathsSetInfo& paths_set_info) {
1216
39
    const auto& parent_indexes = output_schema->inverted_indexs(parent_column->unique_id());
1217
39
    for (const auto& path : nested_paths) {
1218
3
        const auto& find_data_types = path_to_data_types.find(path);
1219
3
        if (find_data_types == path_to_data_types.end() || find_data_types->second.empty()) {
1220
1
            return Status::InternalError("Nested path {} has no data type", path.get_path());
1221
1
        }
1222
2
        DataTypePtr data_type;
1223
2
        get_least_supertype_jsonb(find_data_types->second, &data_type);
1224
1225
2
        const std::string& column_name = parent_column->name_lower_case() + "." + path.get_path();
1226
2
        PathInDataBuilder full_path_builder;
1227
2
        auto full_path = full_path_builder.append(parent_column->name_lower_case(), false)
1228
2
                                 .append(path.get_parts(), false)
1229
2
                                 .build();
1230
2
        TabletColumn nested_column =
1231
2
                get_column_by_type(data_type, column_name,
1232
2
                                   ExtraInfo {.unique_id = -1,
1233
2
                                              .parent_unique_id = parent_column->unique_id(),
1234
2
                                              .path_info = full_path});
1235
2
        inherit_column_attributes(*parent_column, nested_column);
1236
2
        TabletIndexes sub_column_indexes;
1237
2
        inherit_index(parent_indexes, sub_column_indexes, nested_column);
1238
2
        paths_set_info.subcolumn_indexes.emplace(path.get_path(), std::move(sub_column_indexes));
1239
2
        output_schema->append_column(nested_column);
1240
2
        VLOG_DEBUG << "append nested column " << path.get_path();
1241
2
    }
1242
38
    return Status::OK();
1243
39
}
1244
1245
void VariantCompactionUtil::get_compaction_subcolumns_from_subpaths(
1246
        TabletSchema::PathsSetInfo& paths_set_info, const TabletColumnPtr parent_column,
1247
        const TabletSchemaSPtr& target, const PathToDataTypes& path_to_data_types,
1248
43
        const std::unordered_set<std::string>& sparse_paths, TabletSchemaSPtr& output_schema) {
1249
43
    auto& path_set = paths_set_info.sub_path_set;
1250
43
    std::vector<StringRef> sorted_subpaths(path_set.begin(), path_set.end());
1251
43
    std::sort(sorted_subpaths.begin(), sorted_subpaths.end());
1252
43
    const auto& parent_indexes = target->inverted_indexs(parent_column->unique_id());
1253
    // append subcolumns
1254
72
    for (const auto& subpath : sorted_subpaths) {
1255
72
        auto column_name = parent_column->name_lower_case() + "." + subpath.to_string();
1256
72
        auto column_path = make_full_subcolumn_path(parent_column,
1257
72
                                                    std::string_view(subpath.data, subpath.size));
1258
1259
72
        const auto& find_data_types = path_to_data_types.find(PathInData(subpath));
1260
1261
        // some cases: the subcolumn type is variant
1262
        // 1. this path has no data type in segments
1263
        // 2. this path is in sparse paths
1264
        // 3. the sparse paths are too much
1265
72
        TabletSchema::SubColumnInfo sub_column_info;
1266
72
        if (parent_column->variant_enable_typed_paths_to_sparse() &&
1267
72
            generate_sub_column_info(*target, parent_column->unique_id(), std::string(subpath),
1268
16
                                     &sub_column_info)) {
1269
8
            inherit_column_attributes(*parent_column, sub_column_info.column);
1270
8
            output_schema->append_column(sub_column_info.column);
1271
8
            paths_set_info.subcolumn_indexes.emplace(subpath, std::move(sub_column_info.indexes));
1272
8
            VLOG_DEBUG << "append typed column " << subpath;
1273
64
        } else if (find_data_types == path_to_data_types.end() || find_data_types->second.empty() ||
1274
64
                   sparse_paths.find(std::string(subpath)) != sparse_paths.end() ||
1275
64
                   sparse_paths.size() >=
1276
44
                           parent_column->variant_max_sparse_column_statistics_size()) {
1277
27
            TabletColumn subcolumn;
1278
27
            subcolumn.set_name(column_name);
1279
27
            subcolumn.set_type(FieldType::OLAP_FIELD_TYPE_VARIANT);
1280
27
            subcolumn.set_parent_unique_id(parent_column->unique_id());
1281
27
            subcolumn.set_path_info(column_path);
1282
27
            subcolumn.set_aggregation_method(parent_column->aggregation());
1283
27
            subcolumn.set_variant_max_subcolumns_count(
1284
27
                    parent_column->variant_max_subcolumns_count());
1285
27
            subcolumn.set_variant_enable_doc_mode(parent_column->variant_enable_doc_mode());
1286
27
            subcolumn.set_is_nullable(true);
1287
27
            output_schema->append_column(subcolumn);
1288
27
            VLOG_DEBUG << "append sub column " << subpath << " data type "
1289
0
                       << "VARIANT";
1290
27
        }
1291
        // normal case: the subcolumn type can be calculated from the data types in segments
1292
37
        else {
1293
37
            DataTypePtr data_type;
1294
37
            get_least_supertype_jsonb(find_data_types->second, &data_type);
1295
37
            TabletColumn sub_column =
1296
37
                    get_column_by_type(data_type, column_name,
1297
37
                                       ExtraInfo {.unique_id = -1,
1298
37
                                                  .parent_unique_id = parent_column->unique_id(),
1299
37
                                                  .path_info = column_path});
1300
37
            inherit_column_attributes(*parent_column, sub_column);
1301
37
            TabletIndexes sub_column_indexes;
1302
37
            inherit_index(parent_indexes, sub_column_indexes, sub_column);
1303
37
            paths_set_info.subcolumn_indexes.emplace(subpath, std::move(sub_column_indexes));
1304
37
            output_schema->append_column(sub_column);
1305
37
            VLOG_DEBUG << "append sub column " << subpath << " data type " << data_type->get_name();
1306
37
        }
1307
72
    }
1308
43
}
1309
1310
void VariantCompactionUtil::get_compaction_subcolumns_from_data_types(
1311
        TabletSchema::PathsSetInfo& paths_set_info, const TabletColumnPtr parent_column,
1312
        const TabletSchemaSPtr& target, const PathToDataTypes& path_to_data_types,
1313
6
        TabletSchemaSPtr& output_schema) {
1314
6
    const auto& parent_indexes = target->inverted_indexs(parent_column->unique_id());
1315
12
    for (const auto& [path, data_types] : path_to_data_types) {
1316
        // Typed paths are materialized by get_compaction_typed_columns(); this helper only
1317
        // materializes regular subcolumns inferred from rowset data types.
1318
12
        if (data_types.empty() || path.empty() || path.get_is_typed() || path.has_nested_part()) {
1319
5
            continue;
1320
5
        }
1321
7
        DataTypePtr data_type;
1322
7
        get_least_supertype_jsonb(data_types, &data_type);
1323
7
        auto column_name = parent_column->name_lower_case() + "." + path.get_path();
1324
7
        auto column_path = make_full_subcolumn_path(parent_column, path.get_path());
1325
7
        TabletColumn sub_column =
1326
7
                get_column_by_type(data_type, column_name,
1327
7
                                   ExtraInfo {.unique_id = -1,
1328
7
                                              .parent_unique_id = parent_column->unique_id(),
1329
7
                                              .path_info = column_path});
1330
7
        inherit_column_attributes(*parent_column, sub_column);
1331
7
        TabletIndexes sub_column_indexes;
1332
7
        inherit_index(parent_indexes, sub_column_indexes, sub_column);
1333
7
        paths_set_info.sub_path_set.emplace(path.get_path());
1334
7
        paths_set_info.subcolumn_indexes.emplace(path.get_path(), std::move(sub_column_indexes));
1335
7
        output_schema->append_column(sub_column);
1336
7
        VLOG_DEBUG << "append sub column " << path.get_path() << " data type "
1337
0
                   << data_type->get_name();
1338
7
    }
1339
    // The data-type map can contain the variant root as PathInData(), while an empty JSON key
1340
    // only appears in path stats as "". If stats selected it as a materialized path, append it
1341
    // with an explicit empty path part so it does not collide with root.
1342
6
    append_empty_key_subcolumn_from_stats(paths_set_info, parent_column, output_schema);
1343
6
}
1344
1345
// Build the temporary schema for compaction.
1346
// NestedGroup roots are special: the root VARIANT column owns the NG tree and the streaming NG
1347
// writer handles NG children, while regular non-NG paths beside the arrays are materialized as
1348
// ordinary extracted subcolumns. NG typed paths still use get_compaction_typed_columns(), keeping
1349
// typed-column rules out of the NG-specific regular-path filtering.
1350
Status VariantCompactionUtil::get_extended_compaction_schema(
1351
72
        const std::vector<RowsetSharedPtr>& rowsets, TabletSchemaSPtr& target) {
1352
72
    std::unordered_map<int32_t, VariantExtendedInfo> uid_to_variant_extended_info;
1353
72
    const bool needs_variant_extended_info =
1354
882
            std::ranges::any_of(target->columns(), [](const TabletColumnPtr& column) {
1355
882
                return column->is_variant_type() && (should_check_variant_path_stats(*column) ||
1356
35
                                                     column->variant_enable_nested_group());
1357
882
            });
1358
72
    if (needs_variant_extended_info) {
1359
        // collect path stats from all rowsets and segments
1360
69
        for (const auto& rs : rowsets) {
1361
69
            RETURN_IF_ERROR(aggregate_variant_extended_info(rs, &uid_to_variant_extended_info));
1362
69
        }
1363
35
    }
1364
1365
    // build the output schema
1366
72
    TabletSchemaSPtr output_schema = std::make_shared<TabletSchema>();
1367
72
    output_schema->shawdow_copy_without_columns(*target);
1368
72
    std::unordered_map<int32_t, TabletSchema::PathsSetInfo> uid_to_paths_set_info;
1369
72
    const auto ng_root_uids =
1370
72
            collect_nested_group_compaction_root_uids(target, uid_to_variant_extended_info);
1371
891
    for (const TabletColumnPtr& column : target->columns()) {
1372
891
        if (!column->is_extracted_column()) {
1373
890
            output_schema->append_column(*column);
1374
890
        }
1375
891
        if (!column->is_variant_type()) {
1376
852
            continue;
1377
852
        }
1378
39
        VLOG_DEBUG << "column " << column->name() << " unique id " << column->unique_id();
1379
1380
39
        const auto info_it = uid_to_variant_extended_info.find(column->unique_id());
1381
39
        const VariantExtendedInfo empty_extended_info;
1382
39
        const VariantExtendedInfo& extended_info = info_it == uid_to_variant_extended_info.end()
1383
39
                                                           ? empty_extended_info
1384
39
                                                           : info_it->second;
1385
39
        auto& paths_set_info = uid_to_paths_set_info[column->unique_id()];
1386
39
        const bool use_nested_group_compaction_schema = ng_root_uids.contains(column->unique_id());
1387
1388
39
        if (use_nested_group_compaction_schema) {
1389
            // 1. append typed columns. Keep this shared with the non-NG typed helper; only the
1390
            // regular-path selection below is NG-specific.
1391
1
            RETURN_IF_ERROR(get_compaction_typed_columns(target, extended_info.typed_paths, column,
1392
1
                                                         output_schema, paths_set_info));
1393
1394
            // NG roots do not record path-count stats for ordinary Variant paths, so their regular
1395
            // non-NG subcolumns use the same data-types materialization helper as the
1396
            // all-materialized non-NG branch below.
1397
1
            auto regular_path_to_data_types =
1398
1
                    collect_regular_types_outside_nested_group(extended_info);
1399
1
            get_compaction_subcolumns_from_data_types(paths_set_info, column, target,
1400
1
                                                      regular_path_to_data_types, output_schema);
1401
1
            LOG(INFO) << "Variant column uid=" << column->unique_id()
1402
1
                      << " keeps nested-group root and materializes regular non-NG subcolumns in "
1403
1
                         "compaction schema";
1404
1
            continue;
1405
1
        }
1406
1407
38
        if (column->variant_enable_doc_mode()) {
1408
1
            const int bucket_num = std::max(1, column->variant_doc_hash_shard_count());
1409
3
            for (int b = 0; b < bucket_num; ++b) {
1410
2
                TabletColumn doc_value_bucket_column = create_doc_value_column(*column, b);
1411
2
                doc_value_bucket_column.set_type(FieldType::OLAP_FIELD_TYPE_VARIANT);
1412
2
                doc_value_bucket_column.set_is_nullable(false);
1413
2
                doc_value_bucket_column.set_variant_enable_doc_mode(true);
1414
2
                output_schema->append_column(doc_value_bucket_column);
1415
2
            }
1416
1
            continue;
1417
1
        }
1418
1419
        // 1. append typed columns
1420
37
        RETURN_IF_ERROR(get_compaction_typed_columns(target, extended_info.typed_paths, column,
1421
37
                                                     output_schema, paths_set_info));
1422
1423
        // 2. append nested columns
1424
37
        RETURN_IF_ERROR(get_compaction_nested_columns(extended_info.nested_paths,
1425
37
                                                      extended_info.path_to_data_types, column,
1426
37
                                                      output_schema, paths_set_info));
1427
1428
        // 3. get the subpaths
1429
37
        get_subpaths(column->variant_max_subcolumns_count(), extended_info.path_to_none_null_values,
1430
37
                     paths_set_info);
1431
1432
        // 4. append subcolumns
1433
37
        if (column->variant_max_subcolumns_count() > 0 || !column->get_sub_columns().empty()) {
1434
35
            get_compaction_subcolumns_from_subpaths(paths_set_info, column, target,
1435
35
                                                    extended_info.path_to_data_types,
1436
35
                                                    extended_info.sparse_paths, output_schema);
1437
35
        }
1438
        // variant_max_subcolumns_count == 0 and no typed paths materialized
1439
        // it means that all subcolumns are materialized, may be from old data
1440
2
        else {
1441
2
            get_compaction_subcolumns_from_data_types(paths_set_info, column, target,
1442
2
                                                      extended_info.path_to_data_types,
1443
2
                                                      output_schema);
1444
2
        }
1445
1446
        // append sparse column(s)
1447
        // If variant uses bucketized sparse columns, append one sparse bucket column per bucket.
1448
        // Otherwise, append the single sparse column.
1449
37
        int bucket_num = std::max(1, column->variant_sparse_hash_shard_count());
1450
37
        if (bucket_num > 1) {
1451
17
            for (int b = 0; b < bucket_num; ++b) {
1452
12
                TabletColumn sparse_bucket_column = create_sparse_shard_column(*column, b);
1453
12
                output_schema->append_column(sparse_bucket_column);
1454
12
            }
1455
32
        } else {
1456
32
            TabletColumn sparse_column = create_sparse_column(*column);
1457
32
            output_schema->append_column(sparse_column);
1458
32
        }
1459
37
    }
1460
1461
72
    target = output_schema;
1462
    // used to merge & filter path to sparse column during reading in compaction
1463
72
    target->set_path_set_info(std::move(uid_to_paths_set_info));
1464
72
    VLOG_DEBUG << "dump schema " << target->dump_full_schema();
1465
72
    return Status::OK();
1466
72
}
1467
1468
// Calculate statistics about variant data paths from the encoded sparse column
1469
void VariantCompactionUtil::calculate_variant_stats(const IColumn& encoded_sparse_column,
1470
                                                    segment_v2::VariantStatisticsPB* stats,
1471
                                                    size_t max_sparse_column_statistics_size,
1472
37
                                                    size_t row_pos, size_t num_rows) {
1473
    // Cast input column to ColumnMap type since sparse column is stored as a map
1474
37
    const auto& map_column = assert_cast<const ColumnMap&>(encoded_sparse_column);
1475
1476
    // Get the keys column which contains the paths as strings
1477
37
    const auto& sparse_data_paths =
1478
37
            assert_cast<const ColumnString*>(map_column.get_keys_ptr().get());
1479
37
    const auto& serialized_sparse_column_offsets = map_column.get_offsets();
1480
37
    auto& count_map = *stats->mutable_sparse_column_non_null_size();
1481
    // Iterate through all paths in the sparse column
1482
203
    for (size_t i = row_pos; i != row_pos + num_rows; ++i) {
1483
166
        size_t offset = serialized_sparse_column_offsets[i - 1];
1484
166
        size_t end = serialized_sparse_column_offsets[i];
1485
286
        for (size_t j = offset; j != end; ++j) {
1486
120
            auto path = sparse_data_paths->get_data_at(j);
1487
1488
120
            const auto& sparse_path = path.to_string();
1489
            // If path already exists in statistics, increment its count
1490
120
            if (auto it = count_map.find(sparse_path); it != count_map.end()) {
1491
41
                ++it->second;
1492
41
            }
1493
            // If path doesn't exist and we haven't hit the max statistics size limit,
1494
            // add it with count 1
1495
79
            else if (count_map.size() < max_sparse_column_statistics_size) {
1496
43
                count_map.emplace(sparse_path, 1);
1497
43
            }
1498
120
        }
1499
166
    }
1500
1501
37
    if (stats->sparse_column_non_null_size().size() > max_sparse_column_statistics_size) {
1502
0
        throw doris::Exception(
1503
0
                ErrorCode::INTERNAL_ERROR,
1504
0
                "Sparse column non null size: {} is greater than max statistics size: {}",
1505
0
                stats->sparse_column_non_null_size().size(), max_sparse_column_statistics_size);
1506
0
    }
1507
37
}
1508
1509
/// Calculates number of dimensions in array field.
1510
/// Returns 0 for scalar fields.
1511
class FieldVisitorToNumberOfDimensions : public StaticVisitor<size_t> {
1512
public:
1513
    FieldVisitorToNumberOfDimensions() = default;
1514
    template <PrimitiveType T>
1515
2.41M
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
2.41M
        if constexpr (T == TYPE_ARRAY) {
1517
128k
            const size_t size = x.size();
1518
128k
            size_t dimensions = 0;
1519
874k
            for (size_t i = 0; i < size; ++i) {
1520
745k
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
745k
                dimensions = std::max(dimensions, element_dimensions);
1522
745k
            }
1523
128k
            return 1 + dimensions;
1524
2.28M
        } else {
1525
2.28M
            return 0;
1526
2.28M
        }
1527
2.41M
    }
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE1EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
24.0k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
24.0k
        } else {
1525
24.0k
            return 0;
1526
24.0k
        }
1527
24.0k
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE26EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE42EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE7EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
40.9k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
40.9k
        } else {
1525
40.9k
            return 0;
1526
40.9k
        }
1527
40.9k
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE12EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE11EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE25EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE2EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
69.7k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
69.7k
        } else {
1525
69.7k
            return 0;
1526
69.7k
        }
1527
69.7k
    }
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE3EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
6
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
6
        } else {
1525
6
            return 0;
1526
6
        }
1527
6
    }
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE4EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
7
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
7
        } else {
1525
7
            return 0;
1526
7
        }
1527
7
    }
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE5EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
1.24k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
1.24k
        } else {
1525
1.24k
            return 0;
1526
1.24k
        }
1527
1.24k
    }
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE6EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
1.00M
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
1.00M
        } else {
1525
1.00M
            return 0;
1526
1.00M
        }
1527
1.00M
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE38EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE39EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE8EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
1
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
1
        } else {
1525
1
            return 0;
1526
1
        }
1527
1
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE27EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE9EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
164k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
164k
        } else {
1525
164k
            return 0;
1526
164k
        }
1527
164k
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE36EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE37EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE23EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
977k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
977k
        } else {
1525
977k
            return 0;
1526
977k
        }
1527
977k
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE15EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE10EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE41EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE17EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
128k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
128k
        if constexpr (T == TYPE_ARRAY) {
1517
128k
            const size_t size = x.size();
1518
128k
            size_t dimensions = 0;
1519
874k
            for (size_t i = 0; i < size; ++i) {
1520
745k
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
745k
                dimensions = std::max(dimensions, element_dimensions);
1522
745k
            }
1523
128k
            return 1 + dimensions;
1524
        } else {
1525
            return 0;
1526
        }
1527
128k
    }
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE16EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
1
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
1
        } else {
1525
1
            return 0;
1526
1
        }
1527
1
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE18EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE32EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
1
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
1
        } else {
1525
1
            return 0;
1526
1
        }
1527
1
    }
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE28EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE29EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE20EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE30EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE35EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE22EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE19EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE24EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util32FieldVisitorToNumberOfDimensions5applyILNS_13PrimitiveTypeE31EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1515
55
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1516
        if constexpr (T == TYPE_ARRAY) {
1517
            const size_t size = x.size();
1518
            size_t dimensions = 0;
1519
            for (size_t i = 0; i < size; ++i) {
1520
                size_t element_dimensions = apply_visitor(*this, x[i]);
1521
                dimensions = std::max(dimensions, element_dimensions);
1522
            }
1523
            return 1 + dimensions;
1524
55
        } else {
1525
55
            return 0;
1526
55
        }
1527
55
    }
1528
};
1529
1530
// Visitor that allows to get type of scalar field
1531
// but exclude fields contain complex field.This is a faster version
1532
// for FieldVisitorToScalarType which does not support complex field.
1533
class SimpleFieldVisitorToScalarType : public StaticVisitor<size_t> {
1534
public:
1535
    template <PrimitiveType T>
1536
1.58M
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
1.58M
        if constexpr (T == TYPE_ARRAY) {
1538
0
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
12.4k
        } else if constexpr (T == TYPE_NULL) {
1540
12.4k
            have_nulls = true;
1541
12.4k
            return 1;
1542
1.57M
        } else {
1543
1.57M
            type = T;
1544
1.57M
            return 1;
1545
1.57M
        }
1546
1.58M
    }
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE1EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
12.4k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
12.4k
        } else if constexpr (T == TYPE_NULL) {
1540
12.4k
            have_nulls = true;
1541
12.4k
            return 1;
1542
        } else {
1543
            type = T;
1544
            return 1;
1545
        }
1546
12.4k
    }
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE26EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE42EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE7EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
12.3k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
12.3k
        } else {
1543
12.3k
            type = T;
1544
12.3k
            return 1;
1545
12.3k
        }
1546
12.3k
    }
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE12EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE11EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE25EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE2EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
12.4k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
12.4k
        } else {
1543
12.4k
            type = T;
1544
12.4k
            return 1;
1545
12.4k
        }
1546
12.4k
    }
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE3EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
2
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
2
        } else {
1543
2
            type = T;
1544
2
            return 1;
1545
2
        }
1546
2
    }
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE4EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
7
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
7
        } else {
1543
7
            type = T;
1544
7
            return 1;
1545
7
        }
1546
7
    }
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE5EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
718
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
718
        } else {
1543
718
            type = T;
1544
718
            return 1;
1545
718
        }
1546
718
    }
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE6EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
856k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
856k
        } else {
1543
856k
            type = T;
1544
856k
            return 1;
1545
856k
        }
1546
856k
    }
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE38EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE39EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE8EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
1
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
1
        } else {
1543
1
            type = T;
1544
1
            return 1;
1545
1
        }
1546
1
    }
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE27EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE9EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
12.7k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
12.7k
        } else {
1543
12.7k
            type = T;
1544
12.7k
            return 1;
1545
12.7k
        }
1546
12.7k
    }
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE36EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE37EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE23EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
679k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
679k
        } else {
1543
679k
            type = T;
1544
679k
            return 1;
1545
679k
        }
1546
679k
    }
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE15EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE10EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE41EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE17EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE16EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE18EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE32EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE28EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE29EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE20EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE30EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE35EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE22EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE19EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE24EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util30SimpleFieldVisitorToScalarType5applyILNS_13PrimitiveTypeE31EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1536
29
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1537
        if constexpr (T == TYPE_ARRAY) {
1538
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Array type is not supported");
1539
        } else if constexpr (T == TYPE_NULL) {
1540
            have_nulls = true;
1541
            return 1;
1542
29
        } else {
1543
29
            type = T;
1544
29
            return 1;
1545
29
        }
1546
29
    }
1547
1.58M
    void get_scalar_type(PrimitiveType* data_type) const { *data_type = type; }
1548
1.58M
    bool contain_nulls() const { return have_nulls; }
1549
1550
1.58M
    bool need_convert_field() const { return false; }
1551
1552
private:
1553
    PrimitiveType type = PrimitiveType::INVALID_TYPE;
1554
    bool have_nulls = false;
1555
};
1556
1557
/// Visitor that allows to get type of scalar field
1558
/// or least common type of scalars in array.
1559
/// More optimized version of FieldToDataType.
1560
class FieldVisitorToScalarType : public StaticVisitor<size_t> {
1561
public:
1562
    template <PrimitiveType T>
1563
823k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
823k
        if constexpr (T == TYPE_ARRAY) {
1565
128k
            size_t size = x.size();
1566
874k
            for (size_t i = 0; i < size; ++i) {
1567
745k
                apply_visitor(*this, x[i]);
1568
745k
            }
1569
128k
            return 0;
1570
128k
        } else if constexpr (T == TYPE_NULL) {
1571
11.6k
            have_nulls = true;
1572
11.6k
            return 0;
1573
684k
        } else {
1574
684k
            field_types.insert(T);
1575
684k
            type_indexes.insert(T);
1576
684k
            return 0;
1577
684k
        }
1578
823k
    }
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE1EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
11.6k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
11.6k
        } else if constexpr (T == TYPE_NULL) {
1571
11.6k
            have_nulls = true;
1572
11.6k
            return 0;
1573
        } else {
1574
            field_types.insert(T);
1575
            type_indexes.insert(T);
1576
            return 0;
1577
        }
1578
11.6k
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE26EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE42EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE7EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
28.6k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
28.6k
        } else {
1574
28.6k
            field_types.insert(T);
1575
28.6k
            type_indexes.insert(T);
1576
28.6k
            return 0;
1577
28.6k
        }
1578
28.6k
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE12EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE11EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE25EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE2EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
57.2k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
57.2k
        } else {
1574
57.2k
            field_types.insert(T);
1575
57.2k
            type_indexes.insert(T);
1576
57.2k
            return 0;
1577
57.2k
        }
1578
57.2k
    }
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE3EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
4
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
4
        } else {
1574
4
            field_types.insert(T);
1575
4
            type_indexes.insert(T);
1576
4
            return 0;
1577
4
        }
1578
4
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE4EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE5EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
531
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
531
        } else {
1574
531
            field_types.insert(T);
1575
531
            type_indexes.insert(T);
1576
531
            return 0;
1577
531
        }
1578
531
    }
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE6EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
148k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
148k
        } else {
1574
148k
            field_types.insert(T);
1575
148k
            type_indexes.insert(T);
1576
148k
            return 0;
1577
148k
        }
1578
148k
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE38EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE39EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE8EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE27EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE9EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
151k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
151k
        } else {
1574
151k
            field_types.insert(T);
1575
151k
            type_indexes.insert(T);
1576
151k
            return 0;
1577
151k
        }
1578
151k
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE36EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE37EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE23EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
297k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
297k
        } else {
1574
297k
            field_types.insert(T);
1575
297k
            type_indexes.insert(T);
1576
297k
            return 0;
1577
297k
        }
1578
297k
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE15EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE10EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE41EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE17EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
128k
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
128k
        if constexpr (T == TYPE_ARRAY) {
1565
128k
            size_t size = x.size();
1566
874k
            for (size_t i = 0; i < size; ++i) {
1567
745k
                apply_visitor(*this, x[i]);
1568
745k
            }
1569
128k
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
        } else {
1574
            field_types.insert(T);
1575
            type_indexes.insert(T);
1576
            return 0;
1577
        }
1578
128k
    }
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE16EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
1
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
1
        } else {
1574
1
            field_types.insert(T);
1575
1
            type_indexes.insert(T);
1576
1
            return 0;
1577
1
        }
1578
1
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE18EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE32EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
1
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
1
        } else {
1574
1
            field_types.insert(T);
1575
1
            type_indexes.insert(T);
1576
1
            return 0;
1577
1
        }
1578
1
    }
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE28EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE29EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE20EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE30EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE35EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE22EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE19EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Unexecuted instantiation: _ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE24EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
_ZN5doris12variant_util24FieldVisitorToScalarType5applyILNS_13PrimitiveTypeE31EEEmRKNS_19PrimitiveTypeTraitsIXT_EE7CppTypeE
Line
Count
Source
1563
26
    size_t apply(const typename PrimitiveTypeTraits<T>::CppType& x) {
1564
        if constexpr (T == TYPE_ARRAY) {
1565
            size_t size = x.size();
1566
            for (size_t i = 0; i < size; ++i) {
1567
                apply_visitor(*this, x[i]);
1568
            }
1569
            return 0;
1570
        } else if constexpr (T == TYPE_NULL) {
1571
            have_nulls = true;
1572
            return 0;
1573
26
        } else {
1574
26
            field_types.insert(T);
1575
26
            type_indexes.insert(T);
1576
26
            return 0;
1577
26
        }
1578
26
    }
1579
78.1k
    void get_scalar_type(PrimitiveType* type) const {
1580
78.1k
        if (type_indexes.size() == 1) {
1581
            // Most cases will have only one type
1582
64.8k
            *type = *type_indexes.begin();
1583
64.8k
            return;
1584
64.8k
        }
1585
13.3k
        DataTypePtr data_type;
1586
13.3k
        get_least_supertype_jsonb(type_indexes, &data_type);
1587
13.3k
        *type = data_type->get_primitive_type();
1588
13.3k
    }
1589
78.1k
    bool contain_nulls() const { return have_nulls; }
1590
78.1k
    bool need_convert_field() const { return field_types.size() > 1; }
1591
1592
private:
1593
    phmap::flat_hash_set<PrimitiveType> type_indexes;
1594
    phmap::flat_hash_set<PrimitiveType> field_types;
1595
    bool have_nulls = false;
1596
};
1597
1598
template <typename Visitor>
1599
1.66M
void get_field_info_impl(const Field& field, FieldInfo* info) {
1600
1.66M
    Visitor to_scalar_type_visitor;
1601
1.66M
    apply_visitor(to_scalar_type_visitor, field);
1602
1.66M
    PrimitiveType type_id;
1603
1.66M
    to_scalar_type_visitor.get_scalar_type(&type_id);
1604
    // array item's dimension may missmatch, eg. [1, 2, [1, 2, 3]]
1605
1.66M
    *info = {type_id, to_scalar_type_visitor.contain_nulls(),
1606
1.66M
             to_scalar_type_visitor.need_convert_field(),
1607
1.66M
             apply_visitor(FieldVisitorToNumberOfDimensions(), field)};
1608
1.66M
}
_ZN5doris12variant_util19get_field_info_implINS0_24FieldVisitorToScalarTypeEEEvRKNS_5FieldEPNS_9FieldInfoE
Line
Count
Source
1599
78.1k
void get_field_info_impl(const Field& field, FieldInfo* info) {
1600
78.1k
    Visitor to_scalar_type_visitor;
1601
78.1k
    apply_visitor(to_scalar_type_visitor, field);
1602
78.1k
    PrimitiveType type_id;
1603
78.1k
    to_scalar_type_visitor.get_scalar_type(&type_id);
1604
    // array item's dimension may missmatch, eg. [1, 2, [1, 2, 3]]
1605
78.1k
    *info = {type_id, to_scalar_type_visitor.contain_nulls(),
1606
78.1k
             to_scalar_type_visitor.need_convert_field(),
1607
78.1k
             apply_visitor(FieldVisitorToNumberOfDimensions(), field)};
1608
78.1k
}
_ZN5doris12variant_util19get_field_info_implINS0_30SimpleFieldVisitorToScalarTypeEEEvRKNS_5FieldEPNS_9FieldInfoE
Line
Count
Source
1599
1.58M
void get_field_info_impl(const Field& field, FieldInfo* info) {
1600
1.58M
    Visitor to_scalar_type_visitor;
1601
1.58M
    apply_visitor(to_scalar_type_visitor, field);
1602
1.58M
    PrimitiveType type_id;
1603
1.58M
    to_scalar_type_visitor.get_scalar_type(&type_id);
1604
    // array item's dimension may missmatch, eg. [1, 2, [1, 2, 3]]
1605
1.58M
    *info = {type_id, to_scalar_type_visitor.contain_nulls(),
1606
1.58M
             to_scalar_type_visitor.need_convert_field(),
1607
1.58M
             apply_visitor(FieldVisitorToNumberOfDimensions(), field)};
1608
1.58M
}
1609
1610
1.66M
void get_field_info(const Field& field, FieldInfo* info) {
1611
1.66M
    if (field.is_complex_field()) {
1612
78.1k
        get_field_info_impl<FieldVisitorToScalarType>(field, info);
1613
1.58M
    } else {
1614
1.58M
        get_field_info_impl<SimpleFieldVisitorToScalarType>(field, info);
1615
1.58M
    }
1616
1.66M
}
1617
1618
bool generate_sub_column_info(const TabletSchema& schema, int32_t col_unique_id,
1619
                              const std::string& path,
1620
5.68k
                              TabletSchema::SubColumnInfo* sub_column_info) {
1621
5.68k
    const auto& parent_column = schema.column_by_uid(col_unique_id);
1622
5.68k
    std::function<void(const TabletColumn&, TabletColumn*)> generate_result_column =
1623
5.68k
            [&](const TabletColumn& from_column, TabletColumn* to_column) {
1624
262
                to_column->set_name(parent_column.name_lower_case() + "." + path);
1625
262
                to_column->set_type(from_column.type());
1626
262
                to_column->set_parent_unique_id(parent_column.unique_id());
1627
262
                bool is_typed = !parent_column.variant_enable_typed_paths_to_sparse();
1628
262
                to_column->set_path_info(
1629
262
                        PathInData(parent_column.name_lower_case() + "." + path, is_typed));
1630
262
                to_column->set_aggregation_method(parent_column.aggregation());
1631
262
                to_column->set_is_nullable(true);
1632
262
                to_column->set_parent_unique_id(parent_column.unique_id());
1633
262
                if (from_column.is_decimal()) {
1634
0
                    to_column->set_precision(from_column.precision());
1635
0
                }
1636
262
                to_column->set_frac(from_column.frac());
1637
1638
262
                if (from_column.is_array_type()) {
1639
42
                    TabletColumn nested_column;
1640
42
                    generate_result_column(*from_column.get_sub_columns()[0], &nested_column);
1641
42
                    to_column->add_sub_column(nested_column);
1642
42
                }
1643
262
            };
1644
1645
5.68k
    auto generate_index = [&](const std::string& pattern) {
1646
        // 1. find subcolumn's index
1647
220
        if (const auto& indexes = schema.inverted_index_by_field_pattern(col_unique_id, pattern);
1648
220
            !indexes.empty()) {
1649
158
            for (const auto& index : indexes) {
1650
158
                auto index_ptr = std::make_shared<TabletIndex>(*index);
1651
158
                index_ptr->set_escaped_escaped_index_suffix_path(
1652
158
                        sub_column_info->column.path_info_ptr()->get_path());
1653
158
                sub_column_info->indexes.emplace_back(std::move(index_ptr));
1654
158
            }
1655
142
        }
1656
        // 2. find parent column's index
1657
78
        else if (const auto parent_index = schema.inverted_indexs(col_unique_id);
1658
78
                 !parent_index.empty()) {
1659
2
            inherit_index(parent_index, sub_column_info->indexes, sub_column_info->column);
1660
76
        } else {
1661
76
            sub_column_info->indexes.clear();
1662
76
        }
1663
220
    };
1664
1665
5.68k
    const auto& sub_columns = parent_column.get_sub_columns();
1666
5.68k
    for (const auto& sub_column : sub_columns) {
1667
449
        const char* pattern = sub_column->name().c_str();
1668
449
        switch (sub_column->pattern_type()) {
1669
219
        case PatternTypePB::MATCH_NAME: {
1670
219
            if (strcmp(pattern, path.c_str()) == 0) {
1671
111
                generate_result_column(*sub_column, &sub_column_info->column);
1672
111
                generate_index(sub_column->name());
1673
111
                return true;
1674
111
            }
1675
108
            break;
1676
219
        }
1677
230
        case PatternTypePB::MATCH_NAME_GLOB: {
1678
230
            if (glob_match_re2(pattern, path)) {
1679
109
                generate_result_column(*sub_column, &sub_column_info->column);
1680
109
                generate_index(sub_column->name());
1681
109
                return true;
1682
109
            }
1683
121
            break;
1684
230
        }
1685
121
        default:
1686
0
            break;
1687
449
        }
1688
449
    }
1689
5.46k
    return false;
1690
5.68k
}
1691
1692
TabletSchemaSPtr VariantCompactionUtil::calculate_variant_extended_schema(
1693
36
        const std::vector<RowsetSharedPtr>& rowsets, const TabletSchemaSPtr& base_schema) {
1694
36
    if (rowsets.empty()) {
1695
1
        return nullptr;
1696
1
    }
1697
1698
35
    std::vector<TabletSchemaSPtr> schemas;
1699
41
    for (const auto& rs : rowsets) {
1700
41
        if (rs->num_segments() == 0) {
1701
0
            continue;
1702
0
        }
1703
41
        const auto& tablet_schema = rs->tablet_schema();
1704
41
        SegmentCacheHandle segment_cache;
1705
41
        auto st = SegmentLoader::instance()->load_segments(std::static_pointer_cast<BetaRowset>(rs),
1706
41
                                                           &segment_cache);
1707
41
        if (!st.ok()) {
1708
0
            return base_schema;
1709
0
        }
1710
48
        for (const auto& segment : segment_cache.get_segments()) {
1711
48
            TabletSchemaSPtr schema = tablet_schema->copy_without_variant_extracted_columns();
1712
96
            for (const auto& column : tablet_schema->columns()) {
1713
96
                if (!column->is_variant_type()) {
1714
48
                    continue;
1715
48
                }
1716
48
                std::shared_ptr<ColumnReader> column_reader;
1717
48
                OlapReaderStatistics stats;
1718
48
                st = segment->get_column_reader(column->unique_id(), &column_reader, &stats);
1719
48
                if (!st.ok()) {
1720
0
                    LOG(WARNING) << "Failed to get column reader for column: " << column->name()
1721
0
                                 << " error: " << st.to_string();
1722
0
                    continue;
1723
0
                }
1724
48
                if (!column_reader) {
1725
0
                    continue;
1726
0
                }
1727
1728
48
                CHECK(column_reader->get_meta_type() == FieldType::OLAP_FIELD_TYPE_VARIANT);
1729
48
                auto* variant_column_reader =
1730
48
                        assert_cast<segment_v2::VariantColumnReader*>(column_reader.get());
1731
                // load external meta before getting subcolumn meta info
1732
48
                st = variant_column_reader->load_external_meta_once();
1733
48
                if (!st.ok()) {
1734
0
                    LOG(WARNING) << "Failed to load external meta for column: " << column->name()
1735
0
                                 << " error: " << st.to_string();
1736
0
                    continue;
1737
0
                }
1738
48
                const auto* subcolumn_meta_info = variant_column_reader->get_subcolumns_meta_info();
1739
114
                for (const auto& entry : *subcolumn_meta_info) {
1740
114
                    if (entry->path.empty()) {
1741
48
                        continue;
1742
48
                    }
1743
66
                    const std::string& column_name =
1744
66
                            column->name_lower_case() + "." + entry->path.get_path();
1745
66
                    const DataTypePtr& data_type = entry->data.file_column_type;
1746
66
                    PathInDataBuilder full_path_builder;
1747
66
                    auto full_path = full_path_builder.append(column->name_lower_case(), false)
1748
66
                                             .append(entry->path.get_parts(), false)
1749
66
                                             .build();
1750
66
                    TabletColumn subcolumn =
1751
66
                            get_column_by_type(data_type, column_name,
1752
66
                                               ExtraInfo {.unique_id = -1,
1753
66
                                                          .parent_unique_id = column->unique_id(),
1754
66
                                                          .path_info = full_path});
1755
66
                    schema->append_column(subcolumn);
1756
66
                }
1757
48
            }
1758
48
            schemas.emplace_back(schema);
1759
48
        }
1760
41
    }
1761
35
    TabletSchemaSPtr least_common_schema;
1762
35
    auto st = get_least_common_schema(schemas, base_schema, least_common_schema, false);
1763
35
    if (!st.ok()) {
1764
0
        return base_schema;
1765
0
    }
1766
35
    return least_common_schema;
1767
35
}
1768
1769
bool inherit_index(const std::vector<const TabletIndex*>& parent_indexes,
1770
                   TabletIndexes& subcolumns_indexes, FieldType column_type,
1771
1.50k
                   const std::string& suffix_path, bool is_array_nested_type) {
1772
1.50k
    if (parent_indexes.empty()) {
1773
1.45k
        return false;
1774
1.45k
    }
1775
49
    subcolumns_indexes.clear();
1776
    // bkd index or array index only need to inherit one index
1777
49
    if (field_is_numeric_type(column_type) ||
1778
49
        (is_array_nested_type &&
1779
35
         (field_is_numeric_type(column_type) || field_is_slice_type(column_type)))) {
1780
18
        auto index_ptr = std::make_shared<TabletIndex>(*parent_indexes[0]);
1781
18
        index_ptr->set_escaped_escaped_index_suffix_path(suffix_path);
1782
        // no need parse for bkd index or array index
1783
18
        index_ptr->remove_parser_and_analyzer();
1784
18
        subcolumns_indexes.emplace_back(std::move(index_ptr));
1785
18
        return true;
1786
18
    }
1787
    // string type need to inherit all indexes
1788
31
    else if (field_is_slice_type(column_type) && !is_array_nested_type) {
1789
30
        for (const auto& index : parent_indexes) {
1790
30
            auto index_ptr = std::make_shared<TabletIndex>(*index);
1791
30
            index_ptr->set_escaped_escaped_index_suffix_path(suffix_path);
1792
30
            subcolumns_indexes.emplace_back(std::move(index_ptr));
1793
30
        }
1794
29
        return true;
1795
29
    }
1796
2
    return false;
1797
49
}
1798
1799
bool inherit_index(const std::vector<const TabletIndex*>& parent_indexes,
1800
1.51k
                   TabletIndexes& subcolumns_indexes, const TabletColumn& column) {
1801
1.51k
    if (!column.is_extracted_column()) {
1802
3
        return false;
1803
3
    }
1804
1.50k
    if (column.is_array_type()) {
1805
79
        if (column.get_sub_columns().empty()) {
1806
0
            return false;
1807
0
        }
1808
79
        const TabletColumn* nested = column.get_sub_columns()[0].get();
1809
79
        while (nested != nullptr && nested->is_array_type()) {
1810
0
            if (nested->get_sub_columns().empty()) {
1811
0
                return false;
1812
0
            }
1813
0
            nested = nested->get_sub_columns()[0].get();
1814
0
        }
1815
79
        if (nested == nullptr) {
1816
0
            return false;
1817
0
        }
1818
79
        return inherit_index(parent_indexes, subcolumns_indexes, nested->type(),
1819
79
                             column.path_info_ptr()->get_path(), true);
1820
79
    }
1821
1.42k
    return inherit_index(parent_indexes, subcolumns_indexes, column.type(),
1822
1.42k
                         column.path_info_ptr()->get_path());
1823
1.50k
}
1824
1825
bool inherit_index(const std::vector<const TabletIndex*>& parent_indexes,
1826
0
                   TabletIndexes& subcolumns_indexes, const ColumnMetaPB& column_pb) {
1827
0
    if (!column_pb.has_column_path_info()) {
1828
0
        return false;
1829
0
    }
1830
0
    if (column_pb.type() == (int)FieldType::OLAP_FIELD_TYPE_ARRAY) {
1831
0
        if (column_pb.children_columns_size() == 0) {
1832
0
            return false;
1833
0
        }
1834
0
        const ColumnMetaPB* nested = &column_pb.children_columns(0);
1835
0
        while (nested != nullptr && nested->type() == (int)FieldType::OLAP_FIELD_TYPE_ARRAY) {
1836
0
            if (nested->children_columns_size() == 0) {
1837
0
                return false;
1838
0
            }
1839
0
            nested = &nested->children_columns(0);
1840
0
        }
1841
0
        if (nested == nullptr) {
1842
0
            return false;
1843
0
        }
1844
0
        return inherit_index(parent_indexes, subcolumns_indexes, (FieldType)nested->type(),
1845
0
                             column_pb.column_path_info().path(), true);
1846
0
    }
1847
0
    return inherit_index(parent_indexes, subcolumns_indexes, (FieldType)column_pb.type(),
1848
0
                         column_pb.column_path_info().path());
1849
0
}
1850
1851
// ============ Implementation from parse2column.cpp ============
1852
1853
/** Pool for objects that cannot be used from different threads simultaneously.
1854
  * Allows to create an object for each thread.
1855
  * Pool has unbounded size and objects are not destroyed before destruction of pool.
1856
  *
1857
  * Use it in cases when thread local storage is not appropriate
1858
  *  (when maximum number of simultaneously used objects is less
1859
  *   than number of running/sleeping threads, that has ever used object,
1860
  *   and creation/destruction of objects is expensive).
1861
  */
1862
template <typename T>
1863
class SimpleObjectPool {
1864
protected:
1865
    /// Hold all available objects in stack.
1866
    std::mutex mutex;
1867
    std::stack<std::unique_ptr<T>> stack;
1868
    /// Specialized deleter for std::unique_ptr.
1869
    /// Returns underlying pointer back to stack thus reclaiming its ownership.
1870
    struct Deleter {
1871
        SimpleObjectPool<T>* parent;
1872
12.8k
        Deleter(SimpleObjectPool<T>* parent_ = nullptr) : parent {parent_} {} /// NOLINT
1873
12.8k
        void operator()(T* owning_ptr) const {
1874
12.8k
            std::lock_guard lock {parent->mutex};
1875
12.8k
            parent->stack.emplace(owning_ptr);
1876
12.8k
        }
1877
    };
1878
1879
public:
1880
    using Pointer = std::unique_ptr<T, Deleter>;
1881
    /// Extracts and returns a pointer from the stack if it's not empty,
1882
    ///  creates a new one by calling provided f() otherwise.
1883
    template <typename Factory>
1884
12.8k
    Pointer get(Factory&& f) {
1885
12.8k
        std::unique_lock lock(mutex);
1886
12.8k
        if (stack.empty()) {
1887
1
            return {f(), this};
1888
1
        }
1889
12.8k
        auto object = stack.top().release();
1890
12.8k
        stack.pop();
1891
12.8k
        return std::unique_ptr<T, Deleter>(object, Deleter(this));
1892
12.8k
    }
variant_util.cpp:_ZN5doris12variant_util16SimpleObjectPoolINS_14JSONDataParserINS_14SimdJSONParserEEEE3getIZNS0_21parse_json_to_variantERNS_7IColumnERKNS_9StringRefEPS4_RKNS_11ParseConfigEE3$_0EESt10unique_ptrIS4_NS5_7DeleterEEOT_
Line
Count
Source
1884
12.4k
    Pointer get(Factory&& f) {
1885
12.4k
        std::unique_lock lock(mutex);
1886
12.4k
        if (stack.empty()) {
1887
1
            return {f(), this};
1888
1
        }
1889
12.4k
        auto object = stack.top().release();
1890
12.4k
        stack.pop();
1891
12.4k
        return std::unique_ptr<T, Deleter>(object, Deleter(this));
1892
12.4k
    }
variant_util.cpp:_ZN5doris12variant_util16SimpleObjectPoolINS_14JSONDataParserINS_14SimdJSONParserEEEE3getIZNS0_21parse_json_to_variantERNS_7IColumnERKNS_9ColumnStrIjEERKNS_11ParseConfigEE3$_0EESt10unique_ptrIS4_NS5_7DeleterEEOT_
Line
Count
Source
1884
405
    Pointer get(Factory&& f) {
1885
405
        std::unique_lock lock(mutex);
1886
405
        if (stack.empty()) {
1887
0
            return {f(), this};
1888
0
        }
1889
405
        auto object = stack.top().release();
1890
405
        stack.pop();
1891
405
        return std::unique_ptr<T, Deleter>(object, Deleter(this));
1892
405
    }
1893
    /// Like get(), but creates object using default constructor.
1894
    Pointer getDefault() {
1895
        return get([] { return new T; });
1896
    }
1897
};
1898
1899
SimpleObjectPool<JsonParser> parsers_pool;
1900
1901
using Node = typename ColumnVariant::Subcolumns::Node;
1902
1903
150k
static inline void append_binary_bytes(ColumnString::Chars& chars, const void* data, size_t size) {
1904
150k
    const auto old_size = chars.size();
1905
150k
    chars.resize(old_size + size);
1906
150k
    memcpy(chars.data() + old_size, reinterpret_cast<const char*>(data), size);
1907
150k
}
1908
1909
73.3k
static inline void append_binary_type(ColumnString::Chars& chars, FieldType type) {
1910
73.3k
    const uint8_t t = static_cast<uint8_t>(type);
1911
73.3k
    append_binary_bytes(chars, &t, sizeof(uint8_t));
1912
73.3k
}
1913
1914
4.19k
static inline void append_binary_sizet(ColumnString::Chars& chars, size_t v) {
1915
4.19k
    append_binary_bytes(chars, &v, sizeof(size_t));
1916
4.19k
}
1917
1918
73.3k
static void append_field_to_binary_chars(const Field& field, ColumnString::Chars& chars) {
1919
73.3k
    switch (field.get_type()) {
1920
0
    case PrimitiveType::TYPE_NULL: {
1921
0
        append_binary_type(chars, FieldType::OLAP_FIELD_TYPE_NONE);
1922
0
        return;
1923
0
    }
1924
74
    case PrimitiveType::TYPE_BOOLEAN: {
1925
74
        append_binary_type(chars,
1926
74
                           primitive_type_to_storage_field_type(PrimitiveType::TYPE_BOOLEAN));
1927
74
        const auto v = static_cast<UInt8>(field.get<PrimitiveType::TYPE_BOOLEAN>());
1928
74
        append_binary_bytes(chars, &v, sizeof(UInt8));
1929
74
        return;
1930
0
    }
1931
68.9k
    case PrimitiveType::TYPE_BIGINT: {
1932
68.9k
        append_binary_type(chars, primitive_type_to_storage_field_type(PrimitiveType::TYPE_BIGINT));
1933
68.9k
        const auto v = field.get<PrimitiveType::TYPE_BIGINT>();
1934
68.9k
        append_binary_bytes(chars, &v, sizeof(Int64));
1935
68.9k
        return;
1936
0
    }
1937
1
    case PrimitiveType::TYPE_LARGEINT: {
1938
1
        append_binary_type(chars,
1939
1
                           primitive_type_to_storage_field_type(PrimitiveType::TYPE_LARGEINT));
1940
1
        const auto v = field.get<PrimitiveType::TYPE_LARGEINT>();
1941
1
        append_binary_bytes(chars, &v, sizeof(int128_t));
1942
1
        return;
1943
0
    }
1944
49
    case PrimitiveType::TYPE_DOUBLE: {
1945
49
        append_binary_type(chars, primitive_type_to_storage_field_type(PrimitiveType::TYPE_DOUBLE));
1946
49
        const auto v = field.get<PrimitiveType::TYPE_DOUBLE>();
1947
49
        append_binary_bytes(chars, &v, sizeof(Float64));
1948
49
        return;
1949
0
    }
1950
4.13k
    case PrimitiveType::TYPE_STRING: {
1951
4.13k
        append_binary_type(chars, FieldType::OLAP_FIELD_TYPE_STRING);
1952
4.13k
        const auto& v = field.get<PrimitiveType::TYPE_STRING>();
1953
4.13k
        append_binary_sizet(chars, v.size());
1954
4.13k
        append_binary_bytes(chars, v.data(), v.size());
1955
4.13k
        return;
1956
0
    }
1957
1
    case PrimitiveType::TYPE_JSONB: {
1958
1
        append_binary_type(chars, FieldType::OLAP_FIELD_TYPE_JSONB);
1959
1
        const auto& v = field.get<PrimitiveType::TYPE_JSONB>();
1960
1
        append_binary_sizet(chars, v.get_size());
1961
1
        append_binary_bytes(chars, v.get_value(), v.get_size());
1962
1
        return;
1963
0
    }
1964
63
    case PrimitiveType::TYPE_ARRAY: {
1965
63
        append_binary_type(chars, FieldType::OLAP_FIELD_TYPE_ARRAY);
1966
63
        const auto& a = field.get<PrimitiveType::TYPE_ARRAY>();
1967
63
        append_binary_sizet(chars, a.size());
1968
118
        for (const auto& elem : a) {
1969
118
            append_field_to_binary_chars(elem, chars);
1970
118
        }
1971
63
        return;
1972
0
    }
1973
0
    default:
1974
0
        throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Unsupported field type {}",
1975
0
                               field.get_type());
1976
73.3k
    }
1977
73.3k
}
1978
template <typename ParserImpl>
1979
void parse_json_to_variant_impl(IColumn& column, const char* src, size_t length,
1980
108k
                                JSONDataParser<ParserImpl>* parser, const ParseConfig& config) {
1981
108k
    auto& column_variant = assert_cast<ColumnVariant&>(column);
1982
108k
    std::optional<ParseResult> result;
1983
    /// Treat empty string as an empty object
1984
    /// for better CAST from String to Object.
1985
108k
    if (length > 0) {
1986
108k
        result = parser->parse(src, length, config);
1987
108k
    } else {
1988
26
        result = ParseResult {};
1989
26
    }
1990
108k
    if (!result) {
1991
13
        VLOG_DEBUG << "failed to parse " << std::string_view(src, length) << ", length= " << length;
1992
13
        if (config::variant_throw_exeception_on_invalid_json) {
1993
2
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Failed to parse object {}",
1994
2
                                   std::string_view(src, length));
1995
2
        }
1996
        // Treat as string
1997
11
        PathInData root_path;
1998
11
        Field field = Field::create_field<TYPE_STRING>(String(src, length));
1999
11
        result = ParseResult {{root_path}, {field}};
2000
11
    }
2001
108k
    auto& [paths, values] = *result;
2002
108k
    assert(paths.size() == values.size());
2003
107k
    size_t old_num_rows = column_variant.rows();
2004
107k
    if (config.deprecated_enable_flatten_nested) {
2005
        // here we should check the paths in variant and paths in result,
2006
        // if two paths which same prefix have different structure, we should throw an exception
2007
3.00k
        std::vector<PathInData> check_paths;
2008
11.9k
        for (const auto& entry : column_variant.get_subcolumns()) {
2009
11.9k
            check_paths.push_back(entry->path);
2010
11.9k
        }
2011
3.00k
        check_paths.insert(check_paths.end(), paths.begin(), paths.end());
2012
3.00k
        THROW_IF_ERROR(check_variant_has_no_ambiguous_paths(check_paths));
2013
3.00k
    }
2014
107k
    auto [doc_value_data_paths, doc_value_data_values] =
2015
107k
            column_variant.get_doc_value_data_paths_and_values();
2016
107k
    auto& doc_value_data_offsets = column_variant.serialized_doc_value_column_offsets();
2017
2018
1.43M
    auto flush_defaults = [](ColumnVariant::Subcolumn* subcolumn) {
2019
1.43M
        const auto num_defaults = subcolumn->cur_num_of_defaults();
2020
1.43M
        if (num_defaults > 0) {
2021
165k
            subcolumn->insert_many_defaults(num_defaults);
2022
165k
            subcolumn->reset_current_num_of_defaults();
2023
165k
        }
2024
1.43M
    };
2025
2026
107k
    auto is_plain_path = [](const PathInData& path) {
2027
13
        for (const auto& part : path.get_parts()) {
2028
13
            if (part.is_nested || part.anonymous_array_level != 0) {
2029
0
                return false;
2030
0
            }
2031
13
        }
2032
9
        return true;
2033
9
    };
2034
2035
107k
    auto get_or_create_subcolumn = [&](const PathInData& path, size_t index_hint,
2036
1.43M
                                       const FieldInfo& field_info) -> ColumnVariant::Subcolumn* {
2037
1.43M
        auto* subcolumn = column_variant.get_subcolumn(path, index_hint);
2038
1.43M
        if (subcolumn == nullptr) {
2039
3.85k
            if (path.has_nested_part()) {
2040
8
                column_variant.add_nested_subcolumn(path, field_info, old_num_rows);
2041
3.85k
            } else {
2042
3.85k
                column_variant.add_sub_column(path, old_num_rows);
2043
3.85k
            }
2044
3.85k
            subcolumn = column_variant.get_subcolumn(path, index_hint);
2045
3.85k
        }
2046
1.43M
        if (!subcolumn) {
2047
0
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "Failed to find sub column {}",
2048
0
                                   path.get_path());
2049
0
        }
2050
1.43M
        return subcolumn;
2051
1.43M
    };
2052
2053
1.43M
    auto normalize_plain_path = [&](const PathInData& path) {
2054
1.43M
        if (!config.check_duplicate_json_path || path.empty() || !is_plain_path(path)) {
2055
1.43M
            return path;
2056
1.43M
        }
2057
9
        return PathInData(path.get_path());
2058
1.43M
    };
2059
2060
107k
    auto insert_into_subcolumn = [&](size_t i,
2061
1.44M
                                     bool check_size_mismatch) -> ColumnVariant::Subcolumn* {
2062
1.44M
        FieldInfo field_info;
2063
1.44M
        get_field_info(values[i], &field_info);
2064
1.44M
        if (field_info.scalar_type_id == PrimitiveType::INVALID_TYPE) {
2065
122
            return nullptr;
2066
122
        }
2067
1.43M
        auto path = normalize_plain_path(paths[i]);
2068
1.43M
        auto* subcolumn = get_or_create_subcolumn(path, i, field_info);
2069
1.43M
        flush_defaults(subcolumn);
2070
1.43M
        if (check_size_mismatch && subcolumn->size() != old_num_rows) {
2071
1
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT,
2072
1
                                   "subcolumn {} size missmatched, may contains duplicated entry",
2073
1
                                   path.get_path());
2074
1
        }
2075
1.43M
        subcolumn->insert(std::move(values[i]), std::move(field_info));
2076
1.43M
        return subcolumn;
2077
1.43M
    };
2078
2079
107k
    switch (config.parse_to) {
2080
102k
    case ParseConfig::ParseTo::OnlySubcolumns:
2081
1.54M
        for (size_t i = 0; i < paths.size(); ++i) {
2082
1.44M
            insert_into_subcolumn(i, true);
2083
1.44M
        }
2084
102k
        break;
2085
4.72k
    case ParseConfig::ParseTo::OnlyDocValueColumn: {
2086
4.72k
        std::vector<size_t> doc_item_indexes;
2087
4.72k
        doc_item_indexes.reserve(paths.size());
2088
4.72k
        phmap::flat_hash_set<StringRef, StringRefHash> seen_paths;
2089
4.72k
        seen_paths.reserve(paths.size());
2090
2091
77.9k
        for (size_t i = 0; i < paths.size(); ++i) {
2092
73.2k
            FieldInfo field_info;
2093
73.2k
            get_field_info(values[i], &field_info);
2094
73.2k
            if (paths[i].empty()) {
2095
                // Plain non-doc VARIANT can use doc-value KV as writer-side staging. An
2096
                // invalid root entry from JSON object/array is neither a scalar root value nor
2097
                // a doc KV path, so leave this row's doc offset empty. Doc-mode and valid scalar
2098
                // roots still populate the root subcolumn below.
2099
4
                if (!column_variant.enable_doc_mode() &&
2100
4
                    field_info.scalar_type_id == PrimitiveType::INVALID_TYPE) {
2101
1
                    continue;
2102
1
                }
2103
3
                auto* subcolumn = column_variant.get_subcolumn(paths[i]);
2104
3
                DCHECK(subcolumn != nullptr);
2105
3
                flush_defaults(subcolumn);
2106
3
                subcolumn->insert(std::move(values[i]), std::move(field_info));
2107
3
                continue;
2108
4
            }
2109
73.1k
            if (field_info.scalar_type_id == PrimitiveType::INVALID_TYPE ||
2110
73.1k
                values[i].get_type() == PrimitiveType::TYPE_NULL) {
2111
1
                continue;
2112
1
            }
2113
73.1k
            const auto& path_str = paths[i].get_path();
2114
73.1k
            StringRef path_ref {path_str.data(), path_str.size()};
2115
73.1k
            if (UNLIKELY(!seen_paths.emplace(path_ref).second)) {
2116
0
                throw doris::Exception(ErrorCode::INVALID_ARGUMENT,
2117
0
                                       "may contains duplicated entry : {}",
2118
0
                                       std::string_view(path_str));
2119
0
            }
2120
73.1k
            doc_item_indexes.push_back(i);
2121
73.1k
        }
2122
2123
4.72k
        std::sort(doc_item_indexes.begin(), doc_item_indexes.end(),
2124
618k
                  [&](size_t l, size_t r) { return paths[l].get_path() < paths[r].get_path(); });
2125
73.1k
        for (const auto idx : doc_item_indexes) {
2126
73.1k
            const auto& path_str = paths[idx].get_path();
2127
73.1k
            doc_value_data_paths->insert_data(path_str.data(), path_str.size());
2128
73.1k
            auto& chars = doc_value_data_values->get_chars();
2129
73.1k
            append_field_to_binary_chars(values[idx], chars);
2130
73.1k
            doc_value_data_values->get_offsets().push_back(chars.size());
2131
73.1k
        }
2132
4.72k
    } break;
2133
107k
    }
2134
107k
    doc_value_data_offsets.push_back(doc_value_data_paths->size());
2135
    // /// Insert default values to missed subcolumns.
2136
107k
    const auto& subcolumns = column_variant.get_subcolumns();
2137
4.32M
    for (const auto& entry : subcolumns) {
2138
4.32M
        if (entry->data.size() == old_num_rows) {
2139
            // Handle nested paths differently from simple paths
2140
2.88M
            if (entry->path.has_nested_part()) {
2141
                // Try to insert default from nested, if failed, insert regular default
2142
0
                bool success = UNLIKELY(column_variant.try_insert_default_from_nested(entry));
2143
0
                if (!success) {
2144
0
                    entry->data.insert_default();
2145
0
                }
2146
2.88M
            } else {
2147
                // For non-nested paths, increment default counter
2148
2.88M
                entry->data.increment_default_counter();
2149
2.88M
            }
2150
2.88M
        }
2151
4.32M
    }
2152
107k
    column_variant.incr_num_rows();
2153
107k
    if (column_variant.get_sparse_column()->size() == old_num_rows) {
2154
107k
        column_variant.get_sparse_column_mutable().insert_default();
2155
107k
    }
2156
107k
#ifndef NDEBUG
2157
107k
    column_variant.check_consistency();
2158
107k
#endif
2159
107k
}
2160
2161
// exposed interfaces
2162
void parse_json_to_variant(IColumn& column, const StringRef& json, JsonParser* parser,
2163
12.4k
                           const ParseConfig& config) {
2164
12.4k
    if (parser) {
2165
10
        return parse_json_to_variant_impl(column, json.data, json.size, parser, config);
2166
12.4k
    } else {
2167
12.4k
        auto pool_parser = parsers_pool.get([] { return new JsonParser(); });
2168
12.4k
        return parse_json_to_variant_impl(column, json.data, json.size, pool_parser.get(), config);
2169
12.4k
    }
2170
12.4k
}
2171
2172
void parse_json_to_variant(IColumn& column, const ColumnString& raw_json_column,
2173
405
                           const ParseConfig& config) {
2174
405
    auto parser = parsers_pool.get([] { return new JsonParser(); });
2175
96.0k
    for (size_t i = 0; i < raw_json_column.size(); ++i) {
2176
95.6k
        StringRef raw_json = raw_json_column.get_data_at(i);
2177
95.6k
        parse_json_to_variant_impl(column, raw_json.data, raw_json.size, parser.get(), config);
2178
95.6k
    }
2179
405
    column.finalize();
2180
405
}
2181
2182
// parse the doc snapshot column to subcolumns
2183
0
void materialize_docs_to_subcolumns(ColumnVariant& column_variant) {
2184
0
    auto subcolumns = materialize_docs_to_subcolumns_map(column_variant);
2185
2186
0
    for (auto& entry : subcolumns) {
2187
0
        entry.second.finalize();
2188
0
        if (!column_variant.add_sub_column(PathInData(entry.first),
2189
0
                                           IColumn::mutate(entry.second.get_finalized_column_ptr()),
2190
0
                                           entry.second.get_least_common_type())) {
2191
0
            throw doris::Exception(ErrorCode::INTERNAL_ERROR,
2192
0
                                   "Failed to add subcolumn {}, which is from doc snapshot column",
2193
0
                                   entry.first);
2194
0
        }
2195
0
    }
2196
2197
0
    column_variant.finalize();
2198
0
}
2199
2200
// ============ Implementation from variant_util.cpp ============
2201
2202
phmap::flat_hash_map<std::string_view, ColumnVariant::Subcolumn> materialize_docs_to_subcolumns_map(
2203
11
        const ColumnVariant& variant, size_t expected_unique_paths) {
2204
11
    constexpr size_t kInitialPathReserve = 8192;
2205
11
    phmap::flat_hash_map<std::string_view, ColumnVariant::Subcolumn> subcolumns;
2206
2207
11
    const auto [column_key, column_value] = variant.get_doc_value_data_paths_and_values();
2208
11
    const auto& column_offsets = variant.serialized_doc_value_column_offsets();
2209
11
    const size_t num_rows = column_offsets.size();
2210
2211
11
    DCHECK_EQ(num_rows, variant.size()) << "doc snapshot offsets size mismatch with variant rows";
2212
2213
11
    subcolumns.reserve(expected_unique_paths != 0
2214
11
                               ? expected_unique_paths
2215
11
                               : std::min<size_t>(column_key->size(), kInitialPathReserve));
2216
2217
36
    for (size_t row = 0; row < num_rows; ++row) {
2218
25
        const size_t start = column_offsets[row - 1];
2219
25
        const size_t end = column_offsets[row];
2220
71
        for (size_t i = start; i < end; ++i) {
2221
46
            const auto& key = column_key->get_data_at(i);
2222
46
            const std::string_view path_sv(key.data, key.size);
2223
2224
46
            auto [it, inserted] =
2225
46
                    subcolumns.try_emplace(path_sv, ColumnVariant::Subcolumn {0, true, false});
2226
46
            auto& subcolumn = it->second;
2227
46
            if (inserted) {
2228
27
                subcolumn.insert_many_defaults(row);
2229
27
            } else if (subcolumn.size() != row) {
2230
4
                subcolumn.insert_many_defaults(row - subcolumn.size());
2231
4
            }
2232
46
            subcolumn.deserialize_from_binary_column(column_value, i);
2233
46
        }
2234
25
    }
2235
2236
27
    for (auto& [path, subcolumn] : subcolumns) {
2237
27
        if (subcolumn.size() != num_rows) {
2238
7
            subcolumn.insert_many_defaults(num_rows - subcolumn.size());
2239
7
        }
2240
27
    }
2241
2242
11
    return subcolumns;
2243
11
}
2244
2245
Status _parse_and_materialize_variant_columns(Block& block,
2246
                                              const std::vector<uint32_t>& variant_pos,
2247
334
                                              const std::vector<ParseConfig>& configs) {
2248
808
    for (size_t i = 0; i < variant_pos.size(); ++i) {
2249
474
        auto column_ref = block.get_by_position(variant_pos[i]).column;
2250
474
        bool is_nullable = is_column_nullable(*column_ref);
2251
474
        const IColumn& physical_column =
2252
474
                is_nullable ? assert_cast<const ColumnNullable&>(*column_ref).get_nested_column()
2253
474
                            : *column_ref;
2254
474
        const auto* variant_v2 = check_and_get_column<ColumnVariantV2>(physical_column);
2255
474
        if (variant_v2 != nullptr) {
2256
1
            continue;
2257
1
        }
2258
473
        MutableColumnPtr owner_column = IColumn::mutate(std::move(column_ref));
2259
473
        ColumnPtr nullable_null_map;
2260
473
        MutableColumnPtr var_column;
2261
473
        if (is_nullable) {
2262
41
            const auto& nullable = assert_cast<const ColumnNullable&>(*owner_column);
2263
41
            nullable_null_map = nullable.get_null_map_column_ptr();
2264
41
            var_column = IColumn::mutate(nullable.get_nested_column_ptr());
2265
432
        } else {
2266
432
            var_column = std::move(owner_column);
2267
432
        }
2268
473
        auto& var = assert_cast<ColumnVariant&>(*var_column);
2269
473
        var_column->finalize();
2270
2271
473
        MutableColumnPtr variant_column;
2272
473
        if (!var.is_scalar_variant()) {
2273
            // already parsed
2274
380
            continue;
2275
380
        }
2276
2277
93
        VLOG_DEBUG << "parse scalar variant column: " << var.get_root_type()->get_name();
2278
93
        ColumnPtr scalar_root_column;
2279
93
        if (var.get_root_type()->get_primitive_type() == TYPE_JSONB) {
2280
10
            scalar_root_column = jsonb_root_to_json_string_column(*var.get_root());
2281
83
        } else {
2282
83
            const auto& root = *var.get_root();
2283
83
            scalar_root_column =
2284
83
                    is_column_nullable(root)
2285
83
                            ? assert_cast<const ColumnNullable&>(root).get_nested_column_ptr()
2286
83
                            : var.get_root();
2287
83
        }
2288
2289
93
        if (scalar_root_column->is_column_string()) {
2290
92
            variant_column = ColumnVariant::create(0, var.enable_doc_mode());
2291
92
            parse_json_to_variant(*variant_column.get(),
2292
92
                                  assert_cast<const ColumnString&>(*scalar_root_column),
2293
92
                                  configs[i]);
2294
92
        } else {
2295
            // Root maybe other types rather than string like ColumnVariant(Int32).
2296
            // In this case, we should finlize the root and cast to JSON type
2297
1
            auto expected_root_type =
2298
1
                    make_nullable(std::make_shared<ColumnVariant::MostCommonType>());
2299
1
            var.ensure_root_node_type(expected_root_type);
2300
1
            variant_column = std::move(var_column);
2301
1
        }
2302
2303
        // Wrap variant with nullmap if it is nullable
2304
93
        ColumnPtr result = variant_column->get_ptr();
2305
93
        if (is_nullable) {
2306
27
            result = ColumnNullable::create(result, nullable_null_map);
2307
27
        }
2308
93
        block.get_by_position(variant_pos[i]).column = result;
2309
93
    }
2310
334
    return Status::OK();
2311
334
}
2312
2313
Status parse_and_materialize_variant_columns(Block& block, const std::vector<uint32_t>& variant_pos,
2314
334
                                             const std::vector<ParseConfig>& configs) {
2315
334
    RETURN_IF_CATCH_EXCEPTION(
2316
334
            { return _parse_and_materialize_variant_columns(block, variant_pos, configs); });
2317
334
}
2318
2319
namespace {
2320
2321
ParseConfig::ParseTo select_storage_variant_parse_target(const TabletColumn& column,
2322
462
                                                         const ParseConfig& config) {
2323
    // NestedGroup consumes the parse-time subcolumn tree to build nested storage structures, so it
2324
    // must not go through doc-value staging.
2325
462
    if (column.variant_enable_nested_group()) {
2326
0
        return ParseConfig::ParseTo::OnlySubcolumns;
2327
0
    }
2328
2329
    // Persistent doc mode owns doc-value bucket columns in VariantDocWriter. Keep it separate from
2330
    // the plain non-doc staging optimization, even when typed paths or parent indexes exist.
2331
462
    if (column.variant_enable_doc_mode()) {
2332
5
        return ParseConfig::ParseTo::OnlyDocValueColumn;
2333
5
    }
2334
2335
    // Deprecated flatten-nested still consumes parse-time subcolumns. Predefined typed paths and
2336
    // parent inverted indexes are handled later by regular doc-value staging: typed paths are
2337
    // forced into the materialized set unless typed-to-sparse is enabled, and materialized dynamic
2338
    // subcolumns inherit parent indexes while sparse payloads stay unindexed.
2339
457
    if (config.deprecated_enable_flatten_nested) {
2340
0
        return ParseConfig::ParseTo::OnlySubcolumns;
2341
0
    }
2342
2343
    // Plain dynamic non-doc VARIANT can avoid eagerly creating thousands of parse-time subcolumns.
2344
    // The segment writer will pick the materialized/sparse split from this doc-value KV staging.
2345
    // Keep a BE switch so tests and rollouts can compare the old parse-time path with staging under
2346
    // the same writer and schema.
2347
457
    switch (config::variant_storage_parse_mode) {
2348
453
    case 0:
2349
455
    case 2:
2350
455
        return ParseConfig::ParseTo::OnlyDocValueColumn;
2351
2
    case 1:
2352
2
        return ParseConfig::ParseTo::OnlySubcolumns;
2353
0
    default:
2354
0
        CHECK(false) << "invalid variant_storage_parse_mode: "
2355
0
                     << config::variant_storage_parse_mode;
2356
0
        return ParseConfig::ParseTo::OnlyDocValueColumn;
2357
457
    }
2358
457
}
2359
2360
} // namespace
2361
2362
Status parse_and_materialize_variant_columns(Block& block, const TabletSchema& tablet_schema,
2363
322
                                             const std::vector<uint32_t>& column_pos) {
2364
322
    std::vector<uint32_t> variant_column_pos;
2365
322
    std::vector<uint32_t> variant_schema_pos;
2366
322
    variant_column_pos.reserve(column_pos.size());
2367
322
    variant_schema_pos.reserve(column_pos.size());
2368
1.97k
    for (size_t block_pos = 0; block_pos < column_pos.size(); ++block_pos) {
2369
1.64k
        const uint32_t schema_pos = column_pos[block_pos];
2370
1.64k
        const auto& column = tablet_schema.column(schema_pos);
2371
1.64k
        if (column.is_variant_type()) {
2372
462
            variant_column_pos.push_back(schema_pos);
2373
462
            variant_schema_pos.push_back(schema_pos);
2374
462
        }
2375
1.64k
    }
2376
2377
322
    if (variant_column_pos.empty()) {
2378
0
        return Status::OK();
2379
0
    }
2380
2381
322
    std::vector<ParseConfig> configs(variant_column_pos.size());
2382
784
    for (size_t i = 0; i < variant_column_pos.size(); ++i) {
2383
        // Deprecated legacy flatten-nested switch. Distinct from variant_enable_nested_group.
2384
462
        configs[i].deprecated_enable_flatten_nested =
2385
462
                tablet_schema.deprecated_variant_flatten_nested();
2386
462
        configs[i].check_duplicate_json_path = config::variant_enable_duplicate_json_path_check;
2387
462
        const auto& column = tablet_schema.column(variant_schema_pos[i]);
2388
462
        if (!column.is_variant_type()) {
2389
0
            return Status::InternalError("column is not variant type, column name: {}",
2390
0
                                         column.name());
2391
0
        }
2392
462
        configs[i].parse_to = select_storage_variant_parse_target(column, configs[i]);
2393
462
    }
2394
2395
322
    RETURN_IF_ERROR(parse_and_materialize_variant_columns(block, variant_column_pos, configs));
2396
322
    return Status::OK();
2397
322
}
2398
2399
} // namespace doris::variant_util