Coverage Report

Created: 2026-07-25 08:59

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