Coverage Report

Created: 2026-10-09 15:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/function/cast/cast_to_map.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include <array>
19
20
#include "core/column/column_map.h"
21
#include "core/column/column_nullable.h"
22
#include "core/data_type/data_type_map.h"
23
#include "exprs/function/cast/cast_base.h"
24
25
namespace doris::CastWrapper {
26
27
3
inline Status deduplicate_map_keys_in_result(Block& block, uint32_t result) {
28
3
    auto result_column_name = block.get_by_position(result).column->get_name();
29
3
    auto mutable_result_column = IColumn::mutate(std::move(block.get_by_position(result).column));
30
31
3
    if (auto* nullable_column = check_and_get_column<ColumnNullable>(*mutable_result_column)) {
32
3
        auto nested_column = IColumn::mutate(nullable_column->get_nested_column_ptr());
33
3
        auto* map_column = check_and_get_column<ColumnMap>(*nested_column);
34
3
        if (!map_column) {
35
0
            return Status::RuntimeError("Illegal column {} for function CAST AS MAP",
36
0
                                        result_column_name);
37
0
        }
38
39
3
        RETURN_IF_ERROR(map_column->deduplicate_keys(true));
40
3
        ColumnPtr nested_column_ptr = std::move(nested_column);
41
3
        nullable_column->change_nested_column(nested_column_ptr);
42
3
    } else {
43
0
        auto* map_column = check_and_get_column<ColumnMap>(*mutable_result_column);
44
0
        if (!map_column) {
45
0
            return Status::RuntimeError("Illegal column {} for function CAST AS MAP",
46
0
                                        result_column_name);
47
0
        }
48
49
0
        RETURN_IF_ERROR(map_column->deduplicate_keys(true));
50
0
    }
51
52
3
    block.get_by_position(result).column = std::move(mutable_result_column);
53
3
    return Status::OK();
54
3
}
55
56
3
inline WrapperType wrap_string_to_map_wrapper(WrapperType wrapper) {
57
3
    return [wrapper = std::move(wrapper)](FunctionContext* context, Block& block,
58
3
                                          const ColumnNumbers& arguments, uint32_t result,
59
3
                                          size_t input_rows_count,
60
3
                                          const NullMap::value_type* null_map = nullptr) {
61
3
        RETURN_IF_ERROR(wrapper(context, block, arguments, result, input_rows_count, null_map));
62
3
        return deduplicate_map_keys_in_result(block, result);
63
3
    };
64
3
}
65
66
//TODO(Amory) . Need support more cast for key , value for map
67
WrapperType create_map_wrapper(FunctionContext* context, const DataTypePtr& from_type,
68
19
                               const DataTypeMap& to_type) {
69
19
    if (is_string_type(from_type->get_primitive_type())) {
70
3
        auto wrapper = context->enable_strict_mode() ? cast_from_string_to_complex_type_strict_mode
71
3
                                                     : cast_from_string_to_complex_type;
72
3
        return wrap_string_to_map_wrapper(wrapper);
73
3
    }
74
16
    const auto* from = check_and_get_data_type<DataTypeMap>(from_type.get());
75
16
    if (!from) {
76
0
        return CastWrapper::create_unsupport_wrapper(
77
0
                fmt::format("CAST AS Map can only be performed between Map types or from "
78
0
                            "String. from type: {}, to type: {}",
79
0
                            from_type->get_name(), to_type.get_name()));
80
0
    }
81
16
    DataTypes from_kv_types;
82
16
    DataTypes to_kv_types;
83
16
    from_kv_types.reserve(2);
84
16
    to_kv_types.reserve(2);
85
16
    from_kv_types.push_back(from->get_key_type());
86
16
    from_kv_types.push_back(from->get_value_type());
87
16
    to_kv_types.push_back(to_type.get_key_type());
88
16
    to_kv_types.push_back(to_type.get_value_type());
89
90
16
    auto kv_wrappers = get_element_wrappers(context, from_kv_types, to_kv_types);
91
    /// A key or value whose type does not change is passed through, so it needs neither a child mask
92
    /// nor a call into its (identity) wrapper.
93
16
    std::array<bool, 2> unchanged_kv = {from_kv_types[0]->equals(*to_kv_types[0]),
94
16
                                        from_kv_types[1]->equals(*to_kv_types[1])};
95
16
    return [kv_wrappers, from_kv_types, to_kv_types, unchanged_kv](
96
16
                   FunctionContext* context, Block& block, const ColumnNumbers& arguments,
97
16
                   uint32_t result, size_t /*input_rows_count*/,
98
16
                   const NullMap::value_type* null_map = nullptr) -> Status {
99
16
        auto& from_column = block.get_by_position(arguments.front()).column;
100
16
        const auto* from_col_map = check_and_get_column<ColumnMap>(from_column.get());
101
16
        if (!from_col_map) {
102
0
            return Status::RuntimeError("Illegal column {} for function CAST AS MAP",
103
0
                                        from_column->get_name());
104
0
        }
105
106
16
        Columns converted_columns(2);
107
16
        ColumnsWithTypeAndName columnsWithTypeAndName(2);
108
16
        columnsWithTypeAndName[0] = {from_col_map->get_keys_ptr(), from_kv_types[0], ""};
109
16
        columnsWithTypeAndName[1] = {from_col_map->get_values_ptr(), from_kv_types[1], ""};
110
111
16
        const auto& offsets = from_col_map->get_offsets();
112
        // Keys and values share the rows of their parent, so the parent mask is scanned once.
113
16
        const NullMap::value_type* inherited_null_map =
114
16
                (null_map != nullptr && has_masked_row(null_map, offsets.size())) ? null_map
115
16
                                                                                  : nullptr;
116
42
        for (size_t i = 0; i < 2; ++i) {
117
31
            if (unchanged_kv[i]) {
118
14
                converted_columns[i] = columnsWithTypeAndName[i].column;
119
14
                continue;
120
14
            }
121
            /// Keys and values are flattened, so the NULL of a map row has to be inherited by the
122
            /// entries that belong to it before they are cast.
123
17
            auto child_mask = build_child_null_mask(inherited_null_map, &offsets,
124
17
                                                    columnsWithTypeAndName[i].column);
125
17
            ColumnWithTypeAndName child_column {child_mask.column, from_kv_types[i], ""};
126
17
            ColumnNumbers element_arguments {block.columns()};
127
17
            block.insert(child_column);
128
17
            auto element_result = block.columns();
129
17
            block.insert({to_kv_types[i], ""});
130
17
            RETURN_IF_ERROR(kv_wrappers[i](context, block, element_arguments, element_result,
131
17
                                           child_mask.column->size(), child_mask.null_map));
132
12
            converted_columns[i] = block.get_by_position(element_result).column;
133
12
        }
134
135
11
        auto map_column = ColumnMap::create(converted_columns[0], converted_columns[1],
136
11
                                            from_col_map->get_offsets_ptr());
137
11
        static_cast<void>(map_column->deduplicate_keys());
138
11
        block.get_by_position(result).column = std::move(map_column);
139
11
        return Status::OK();
140
16
    };
141
16
}
142
} // namespace doris::CastWrapper