Coverage Report

Created: 2026-10-23 10:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/function/function_string_misc.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 <crc32c/crc32c.h>
19
#include <fmt/format.h>
20
#include <glog/logging.h>
21
#include <unicode/normalizer2.h>
22
#include <unicode/stringpiece.h>
23
#include <unicode/unistr.h>
24
25
#include <algorithm>
26
#include <bit>
27
#include <boost/locale.hpp>
28
#include <cctype>
29
#include <climits>
30
#include <cstddef>
31
#include <cstdint>
32
#include <cstdlib>
33
#include <cstring>
34
#include <format>
35
#include <iomanip>
36
#include <memory>
37
#include <random>
38
#include <sstream>
39
#include <string>
40
#include <string_view>
41
#include <unordered_map>
42
#include <utility>
43
#include <vector>
44
45
#include "common/compiler_util.h"
46
#include "common/exception.h"
47
#include "common/status.h"
48
#include "core/assert_cast.h"
49
#include "core/block/block.h"
50
#include "core/block/column_numbers.h"
51
#include "core/block/column_with_type_and_name.h"
52
#include "core/column/column.h"
53
#include "core/column/column_const.h"
54
#include "core/column/column_nullable.h"
55
#include "core/column/column_string.h"
56
#include "core/column/column_vector.h"
57
#include "core/data_type/data_type.h"
58
#include "core/data_type/data_type_nullable.h"
59
#include "core/data_type/data_type_number.h"
60
#include "core/data_type/data_type_string.h"
61
#include "core/data_type/define_primitive_type.h"
62
#include "core/memcpy_small.h"
63
#include "core/pod_array.h"
64
#include "core/string_ref.h"
65
#include "core/types.h"
66
#include "exec/common/hash_table/phmap_fwd_decl.h"
67
#include "exec/common/pinyin.h"
68
#include "exec/common/stringop_substring.h"
69
#include "exec/common/template_helpers.hpp"
70
#include "exprs/function/function.h"
71
#include "exprs/function/function_helpers.h"
72
#include "exprs/function/function_needs_to_handle_null.h"
73
#include "exprs/function_context.h"
74
#include "pugixml.hpp"
75
#include "util/hash_util.hpp"
76
#include "util/raw_value.h"
77
#include "util/simd/vstring_function.h"
78
#include "util/string_util.h"
79
#include "util/utf8_check.h"
80
81
#ifndef USE_LIBCPP
82
#include <memory_resource>
83
#define PMR std::pmr
84
#else
85
#include <boost/container/pmr/monotonic_buffer_resource.hpp>
86
#include <boost/container/pmr/vector.hpp>
87
#define PMR boost::container::pmr
88
#endif
89
90
#include "exprs/function/simple_function_factory.h"
91
92
namespace doris {
93
#include "common/compile_check_avoid_begin.h"
94
95
class FunctionAutoPartitionName : public IFunction {
96
public:
97
    static constexpr auto name = "auto_partition_name";
98
89
    static FunctionPtr create() { return std::make_shared<FunctionAutoPartitionName>(); }
99
0
    String get_name() const override { return name; }
100
0
    size_t get_number_of_arguments() const override { return 0; }
101
81
    bool is_variadic() const override { return true; }
102
188
    bool use_default_implementation_for_nulls() const override { return false; }
103
80
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
104
80
        return std::make_shared<DataTypeString>();
105
80
    }
106
107
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
108
108
                        uint32_t result, size_t input_rows_count) const override {
109
108
        size_t argument_size = arguments.size();
110
108
        auto const_null_map = ColumnUInt8::create(input_rows_count, 0);
111
108
        auto null_map = ColumnUInt8::create(input_rows_count, 0);
112
108
        std::vector<const ColumnString::Chars*> chars_list(argument_size);
113
108
        std::vector<const ColumnString::Offsets*> offsets_list(argument_size);
114
108
        std::vector<bool> is_const_args(argument_size);
115
108
        std::vector<const ColumnUInt8::Container*> null_list(argument_size);
116
108
        std::vector<ColumnPtr> argument_null_columns(argument_size);
117
118
108
        std::vector<ColumnPtr> argument_columns(argument_size);
119
421
        for (int i = 0; i < argument_size; ++i) {
120
313
            argument_columns[i] =
121
313
                    block.get_by_position(arguments[i]).column->convert_to_full_column_if_const();
122
313
            if (const auto* nullable =
123
313
                        check_and_get_column<const ColumnNullable>(*argument_columns[i])) {
124
37
                null_list[i] = &nullable->get_null_map_data();
125
37
                argument_null_columns[i] = nullable->get_null_map_column_ptr();
126
37
                argument_columns[i] = nullable->get_nested_column_ptr();
127
276
            } else {
128
276
                null_list[i] = &const_null_map->get_data();
129
276
            }
130
131
313
            const auto& [col, is_const] =
132
313
                    unpack_if_const(block.get_by_position(arguments[i]).column);
133
134
313
            const auto* col_str = assert_cast<const ColumnString*>(argument_columns[i].get());
135
313
            chars_list[i] = &col_str->get_chars();
136
313
            offsets_list[i] = &col_str->get_offsets();
137
313
            is_const_args[i] = is_const;
138
313
        }
139
140
108
        auto res = ColumnString::create();
141
108
        auto& res_data = res->get_chars();
142
108
        auto& res_offset = res->get_offsets();
143
108
        res_offset.resize(input_rows_count);
144
145
108
        std::string partition_type(chars_list[0]->raw_data(), (*offsets_list[0])[0]);
146
108
        std::transform(partition_type.begin(), partition_type.end(), partition_type.begin(),
147
486
                       [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
148
        // partition type is list|range
149
108
        if (partition_type == "list") {
150
54
            return _auto_partition_type_of_list(chars_list, offsets_list, is_const_args, null_list,
151
54
                                                res_data, res_offset, input_rows_count,
152
54
                                                argument_size, block, result, res);
153
54
        } else {
154
54
            return _auto_partition_type_of_range(chars_list, offsets_list, is_const_args, res_data,
155
54
                                                 res_offset, input_rows_count, argument_size, block,
156
54
                                                 result, res);
157
54
        }
158
0
        return Status::OK();
159
108
    }
160
161
private:
162
79
    std::u16string _string_to_u16string(const std::string& str) const {
163
79
        return boost::locale::conv::utf_to_utf<char16_t>(str);
164
79
    }
165
166
79
    std::string _string_to_unicode(const std::u16string& s) const {
167
79
        std::string res_s;
168
79
        res_s.reserve(s.size());
169
79
        if (s.length() > 0 && s[0] == '-') {
170
1
            res_s += '_';
171
1
        }
172
1.08k
        for (int i = 0; i < s.length(); i++) {
173
1.00k
            char16_t ch = s[i];
174
1.00k
            if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
175
606
                res_s += ch;
176
606
            } else {
177
398
                int unicodeValue = _get_code_point_at(s, i);
178
398
                res_s += fmt::format("{:02x}", static_cast<uint32_t>(unicodeValue));
179
398
            }
180
1.00k
        }
181
79
        return res_s;
182
79
    }
183
184
398
    int _get_code_point_at(const std::u16string& str, std::size_t index) const {
185
398
        char16_t first = str[index];
186
        // [0xD800,0xDBFF] is the scope of the first code unit
187
398
        if ((first >= 0xD800 && first <= 0xDBFF) && (index + 1 < str.size())) {
188
0
            char16_t second = str[index + 1];
189
            // [0xDC00,0xDFFF] is the scope of the second code unit
190
0
            if (second >= 0xDC00 && second <= 0xDFFF) {
191
0
                return ((first - 0xD800) << 10) + (second - 0xDC00) + 0x10000;
192
0
            }
193
0
        }
194
195
398
        return first;
196
398
    }
197
    Status _auto_partition_type_of_list(std::vector<const ColumnString::Chars*>& chars_list,
198
                                        std::vector<const ColumnString::Offsets*>& offsets_list,
199
                                        std::vector<bool>& is_const_args,
200
                                        const std::vector<const ColumnUInt8::Container*>& null_list,
201
                                        auto& res_data, auto& res_offset, size_t input_rows_count,
202
                                        size_t argument_size, Block& block, uint32_t result,
203
54
                                        auto& res) const {
204
54
        int curr_len = 0;
205
108
        for (int row = 0; row < input_rows_count; row++) {
206
54
            std::string res_p;
207
54
            res_p.reserve(argument_size * 5);
208
54
            res_p += 'p';
209
151
            for (int col = 1; col < argument_size; col++) {
210
97
                const auto& current_offsets = *offsets_list[col];
211
97
                const auto& current_chars = *chars_list[col];
212
97
                const auto& current_nullmap = *null_list[col];
213
214
97
                if (current_nullmap[row]) {
215
18
                    res_p += 'X';
216
79
                } else {
217
79
                    auto idx = index_check_const(row, is_const_args[col]);
218
219
79
                    int size = current_offsets[idx] - current_offsets[idx - 1];
220
79
                    const char* raw_chars =
221
79
                            reinterpret_cast<const char*>(&current_chars[current_offsets[idx - 1]]);
222
                    // convert string to u16string in order to convert to unicode strings
223
79
                    const std::string raw_str(raw_chars, size);
224
79
                    auto u16string = _string_to_u16string(raw_str);
225
79
                    res_p += _string_to_unicode(u16string) + std::to_string(u16string.size());
226
79
                }
227
97
            }
228
229
            // check the name of length
230
54
            int len = res_p.size();
231
54
            if (len > 50) {
232
7
                res_p = std::format("{}_{:08x}", res_p.substr(0, 50), to_hash_code(res_p));
233
7
                len = res_p.size();
234
7
            }
235
54
            curr_len += len;
236
54
            res_data.resize(curr_len);
237
54
            memcpy(&res_data[res_offset[row - 1]], res_p.c_str(), len);
238
54
            res_offset[row] = res_offset[row - 1] + len;
239
54
        }
240
54
        block.get_by_position(result).column = std::move(res);
241
54
        return Status::OK();
242
54
    }
243
244
    size_t _copy_date_str_of_len_to_res_data(auto& res_data, auto& res_offset,
245
                                             std::vector<std::string>& date_str, size_t row,
246
96
                                             size_t len) const {
247
96
        size_t curr_len = 1;
248
408
        for (int j = 0; j < len; j++) {
249
312
            memcpy(&res_data[res_offset[row - 1]] + curr_len, date_str[j].c_str(),
250
312
                   date_str[j].size());
251
312
            curr_len += date_str[j].size();
252
312
        }
253
96
        return curr_len;
254
96
    }
255
256
    Status _auto_partition_type_of_range(std::vector<const ColumnString::Chars*>& chars_list,
257
                                         std::vector<const ColumnString::Offsets*>& offsets_list,
258
                                         std::vector<bool>& is_const_args, auto& res_data,
259
                                         auto& res_offset, size_t input_rows_count,
260
                                         size_t argument_size, Block& block, uint32_t result,
261
54
                                         auto& res) const {
262
54
        std::string range_type(chars_list[1]->raw_data(), (*offsets_list[1])[0]);
263
54
        std::transform(range_type.begin(), range_type.end(), range_type.begin(),
264
243
                       [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
265
266
54
        res_data.resize(15 * input_rows_count);
267
150
        for (int i = 0; i < input_rows_count; i++) {
268
102
            const auto& current_offsets = *offsets_list[2];
269
102
            const auto& current_chars = *chars_list[2];
270
271
102
            auto idx = index_check_const(i, is_const_args[2]);
272
102
            int size = current_offsets[idx] - current_offsets[idx - 1];
273
102
            const char* tmp =
274
102
                    reinterpret_cast<const char*>(&current_chars[current_offsets[idx - 1]]);
275
102
            std::string to_split_s(tmp, size);
276
277
            // check the str if it is date|datetime
278
102
            RE2 date_regex(R"(^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}:\d{2})?$)");
279
102
            if (!RE2::FullMatch(to_split_s, date_regex)) {
280
6
                return Status::InvalidArgument("The range partition only support DATE|DATETIME");
281
6
            }
282
283
            // split date_str from (yyyy-mm-dd hh:mm:ss) to ([yyyy, mm, dd, hh, mm, ss])
284
96
            std::vector<std::string> date_str(6);
285
96
            date_str[0] = to_split_s.substr(0, 4);
286
348
            for (int ni = 5, j = 1; ni <= size; ni += 3, j++) {
287
252
                date_str[j] = to_split_s.substr(ni, 2);
288
252
            }
289
96
            int curr_len = 0;
290
291
96
            res_data[res_offset[i - 1]] = 'p';
292
            // raw => 2022-12-12 11:30:20
293
            // year => 2022 01 01 00 00 00
294
            // month => 2022 12 01 00 00 00
295
            // day => 2022 12 12 00 00 00
296
            // hour => 2022 12 12 11 00 00
297
            // minute => 2022 12  11 30 00
298
            // second => 2022 12 12 12 30 20
299
300
96
            if (range_type == "year") {
301
17
                curr_len += _copy_date_str_of_len_to_res_data(res_data, res_offset, date_str, i, 1);
302
17
                memcpy(&res_data[res_offset[i - 1]] + curr_len, "0101", 4);
303
17
                curr_len += 4;
304
79
            } else if (range_type == "month") {
305
20
                curr_len += _copy_date_str_of_len_to_res_data(res_data, res_offset, date_str, i, 2);
306
20
                memcpy(&res_data[res_offset[i - 1]] + curr_len, "01", 2);
307
20
                curr_len += 2;
308
59
            } else if (range_type == "day") {
309
20
                curr_len += _copy_date_str_of_len_to_res_data(res_data, res_offset, date_str, i, 3);
310
39
            } else if (range_type == "hour") {
311
13
                curr_len += _copy_date_str_of_len_to_res_data(res_data, res_offset, date_str, i, 4);
312
26
            } else if (range_type == "minute") {
313
13
                curr_len += _copy_date_str_of_len_to_res_data(res_data, res_offset, date_str, i, 5);
314
13
            } else if (range_type == "second") {
315
13
                curr_len += _copy_date_str_of_len_to_res_data(res_data, res_offset, date_str, i, 6);
316
13
            }
317
318
            // fill in zero
319
96
            int zero = 15 - curr_len;
320
96
            std::fill_n(&res_data[res_offset[i - 1]] + curr_len, zero, '0');
321
96
            curr_len += zero;
322
96
            res_offset[i] = res_offset[i - 1] + curr_len;
323
96
        }
324
48
        block.get_by_position(result).column = std::move(res);
325
48
        return Status::OK();
326
54
    }
327
328
7
    int32_t to_hash_code(const std::string& str) const {
329
7
        uint64_t h = 0;
330
1.37k
        for (uint8_t c : str) {
331
1.37k
            h = (h * 31U + c) & 0xFFFFFFFFU;
332
1.37k
        }
333
7
        return static_cast<int32_t>(h);
334
7
    }
335
};
336
337
class FunctionRandomBytes : public IFunction {
338
public:
339
    static constexpr auto name = "random_bytes";
340
14
    static FunctionPtr create() { return std::make_shared<FunctionRandomBytes>(); }
341
1
    String get_name() const override { return name; }
342
5
    size_t get_number_of_arguments() const override { return 1; }
343
6
    bool is_variadic() const override { return false; }
344
345
5
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
346
5
        return std::make_shared<DataTypeString>();
347
5
    }
348
349
15
    bool use_default_implementation_for_constants() const final { return false; }
350
351
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
352
4
                        uint32_t result, size_t input_rows_count) const override {
353
4
        auto res = ColumnString::create();
354
4
        auto& res_offsets = res->get_offsets();
355
4
        auto& res_chars = res->get_chars();
356
4
        res_offsets.resize(input_rows_count);
357
358
4
        auto [arg_col, arg_const] = unpack_if_const(block.get_by_position(arguments[0]).column);
359
4
        const auto* length_col = assert_cast<const ColumnInt32*>(arg_col.get());
360
361
4
        if (arg_const) {
362
3
            res_chars.reserve(input_rows_count * (length_col->get_element(0) + 2));
363
3
        }
364
365
4
        std::vector<uint8_t, Allocator_<uint8_t>> random_bytes;
366
4
        std::random_device rd;
367
4
        std::mt19937 gen(rd());
368
369
4
        std::uniform_int_distribution<unsigned short> distribution(0, 255);
370
19
        for (size_t i = 0; i < input_rows_count; ++i) {
371
16
            size_t index = index_check_const(i, arg_const);
372
16
            if (length_col->get_element(index) < 0) [[unlikely]] {
373
1
                return Status::InvalidArgument("argument {} of function {} at row {} was invalid.",
374
1
                                               length_col->get_element(index), name, index);
375
1
            }
376
15
            random_bytes.resize(length_col->get_element(index));
377
378
117
            for (auto& byte : random_bytes) {
379
117
                byte = distribution(gen) & 0xFF;
380
117
            }
381
382
15
            std::basic_ostringstream<char, std::char_traits<char>, Allocator_<char>> oss;
383
117
            for (const auto& byte : random_bytes) {
384
117
                oss << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(byte);
385
117
            }
386
387
15
            StringOP::push_value_string("0x" + oss.str(), i, res_chars, res_offsets);
388
15
            random_bytes.clear();
389
15
        }
390
391
3
        block.get_by_position(result).column = std::move(res);
392
393
3
        return Status::OK();
394
4
    }
395
};
396
397
class FunctionConvertTo : public IFunction {
398
public:
399
    static constexpr auto name = "convert_to";
400
401
15
    static FunctionPtr create() { return std::make_shared<FunctionConvertTo>(); }
402
403
1
    String get_name() const override { return name; }
404
405
6
    size_t get_number_of_arguments() const override { return 2; }
406
407
6
    DataTypePtr get_return_type_impl(const DataTypes& /*arguments*/) const override {
408
6
        return std::make_shared<DataTypeString>();
409
6
    }
410
411
29
    Status open(FunctionContext* context, FunctionContext::FunctionStateScope scope) override {
412
29
        if (scope != FunctionContext::FunctionStateScope::THREAD_LOCAL) {
413
6
            return Status::OK();
414
6
        }
415
23
        if (!context->is_col_constant(1)) {
416
0
            return Status::InvalidArgument(
417
0
                    "character argument to convert function must be constant.");
418
0
        }
419
23
        const auto& character_data = context->get_constant_col(1)->column_ptr->get_data_at(0);
420
23
        if (!iequal(character_data.to_string(), "gbk")) {
421
0
            return Status::RuntimeError(
422
0
                    "Illegal second argument column of function convert. now only support "
423
0
                    "convert to character set of gbk");
424
0
        }
425
426
23
        return Status::OK();
427
23
    }
428
429
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
430
14
                        uint32_t result, size_t input_rows_count) const override {
431
14
        ColumnPtr argument_column =
432
14
                block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
433
14
        const ColumnString* str_col = static_cast<const ColumnString*>(argument_column.get());
434
14
        const auto& str_offset = str_col->get_offsets();
435
14
        const auto& str_chars = str_col->get_chars();
436
14
        auto col_res = ColumnString::create();
437
14
        auto& res_offset = col_res->get_offsets();
438
14
        auto& res_chars = col_res->get_chars();
439
14
        res_offset.resize(input_rows_count);
440
        // max pinyin size is 6 + 1 (first '~') for utf8 chinese word 3
441
14
        size_t pinyin_size = (str_chars.size() + 2) / 3 * 7;
442
14
        ColumnString::check_chars_length(pinyin_size, 0);
443
14
        res_chars.resize(pinyin_size);
444
445
14
        size_t in_len = 0, out_len = 0;
446
49
        for (int i = 0; i < input_rows_count; ++i) {
447
35
            in_len = str_offset[i] - str_offset[i - 1];
448
35
            const char* in = reinterpret_cast<const char*>(&str_chars[str_offset[i - 1]]);
449
35
            char* out = reinterpret_cast<char*>(&res_chars[res_offset[i - 1]]);
450
35
            _utf8_to_pinyin(in, in_len, out, &out_len);
451
35
            res_offset[i] = res_offset[i - 1] + out_len;
452
35
        }
453
14
        res_chars.resize(res_offset[input_rows_count - 1]);
454
14
        block.replace_by_position(result, std::move(col_res));
455
14
        return Status::OK();
456
14
    }
457
458
35
    void _utf8_to_pinyin(const char* in, size_t in_len, char* out, size_t* out_len) const {
459
225
        auto do_memcpy = [](char*& dest, const char*& from, size_t size) {
460
225
            memcpy_small_allow_read_write_overflow15(dest, from, size);
461
225
            dest += size;
462
225
            from += size;
463
225
        };
464
35
        auto from = in;
465
35
        auto dest = out;
466
467
273
        while (from - in < in_len) {
468
238
            auto length = get_utf8_byte_length(*from);
469
238
            if (length != 3) {
470
225
                do_memcpy(dest, from, length);
471
225
            } else {
472
                // convert utf8 to unicode code to get pinyin offset
473
13
                if (auto tmp = (((int)(*from & 0x0F)) << 12) | (((int)(*(from + 1) & 0x3F)) << 6) |
474
13
                               (*(from + 2) & 0x3F);
475
13
                    tmp >= START_UNICODE_OFFSET and tmp < END_UNICODE_OFFSET) {
476
13
                    const char* buf = nullptr;
477
13
                    if (tmp >= START_UNICODE_OFFSET && tmp < MID_UNICODE_OFFSET) {
478
2
                        buf = PINYIN_DICT1 + (tmp - START_UNICODE_OFFSET) * MAX_PINYIN_LEN;
479
11
                    } else if (tmp >= MID_UNICODE_OFFSET && tmp < END_UNICODE_OFFSET) {
480
11
                        buf = PINYIN_DICT2 + (tmp - MID_UNICODE_OFFSET) * MAX_PINYIN_LEN;
481
11
                    }
482
483
13
                    auto end = strchr(buf, ' ');
484
                    // max len for pinyin is 6
485
13
                    int len = MAX_PINYIN_LEN;
486
13
                    if (end != nullptr && end - buf < MAX_PINYIN_LEN) {
487
3
                        len = end - buf;
488
3
                    }
489
                    // set first char '~' just make sure all english word lower than chinese word
490
13
                    *dest = 126;
491
13
                    memcpy(dest + 1, buf, len);
492
13
                    dest += (len + 1);
493
13
                    from += 3;
494
13
                } else {
495
0
                    do_memcpy(dest, from, 3);
496
0
                }
497
13
            }
498
238
        }
499
500
35
        *out_len = dest - out;
501
35
    }
502
};
503
// +-----------------------------------+
504
// | 丝                                |
505
// +-----------------------------------+
506
// 1 row in set, 1 warning (0.00 sec)
507
// mysql> select char(14989469 using utf8);
508
// +---------------------------+
509
// | char(14989469 using utf8) |
510
// +---------------------------+
511
// | 丝                        |
512
// +---------------------------+
513
// 1 row in set, 1 warning (0.00 sec)
514
// mysql> select char(0xe5, 0xa4, 0x9a, 0xe7, 0x9d, 0xbf, 0xe4, 0xb8, 0x9d, 68, 111, 114, 105, 115 using utf8);
515
// +---------------------------------------------------------------------------------------------+
516
// | char(0xe5, 0xa4, 0x9a, 0xe7, 0x9d, 0xbf, 0xe4, 0xb8, 0x9d, 68, 111, 114, 105, 115 using utf8) |
517
// +---------------------------------------------------------------------------------------------+
518
// | 多睿丝 Doris                                                                                 |
519
// +---------------------------------------------------------------------------------------------+
520
// mysql> select char(68, 111, 114, 0, 105, null, 115 using utf8);
521
// +--------------------------------------------------+
522
// | char(68, 111, 114, 0, 105, null, 115 using utf8) |
523
// +--------------------------------------------------+
524
// | Dor is                                           |
525
// +--------------------------------------------------+
526
527
// return null:
528
// mysql>  select char(255 using utf8);
529
// +----------------------+
530
// | char(255 using utf8) |
531
// +----------------------+
532
// | NULL                 |
533
// +----------------------+
534
// 1 row in set, 2 warnings (0.00 sec)
535
//
536
// mysql> show warnings;
537
// +---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
538
// | Level   | Code | Message                                                                                                                                                                     |
539
// +---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
540
// | Warning | 3719 | 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. |
541
// | Warning | 1300 | Invalid utf8mb3 character string: 'FF'                                                                                                                                      |
542
// +---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
543
// 2 rows in set (0.01 sec)
544
545
// max int value:
546
// mysql> select char(18446744073709551615);
547
// +--------------------------------------------------------+
548
// | char(18446744073709551615)                             |
549
// +--------------------------------------------------------+
550
// | 0xFFFFFFFF                                             |
551
// +--------------------------------------------------------+
552
// 1 row in set (0.00 sec)
553
//
554
// mysql> select char(18446744073709551616);
555
// +--------------------------------------------------------+
556
// | char(18446744073709551616)                             |
557
// +--------------------------------------------------------+
558
// | 0xFFFFFFFF                                             |
559
// +--------------------------------------------------------+
560
// 1 row in set, 1 warning (0.00 sec)
561
//
562
// mysql> show warnings;
563
// +---------+------+-----------------------------------------------------------+
564
// | Level   | Code | Message                                                   |
565
// +---------+------+-----------------------------------------------------------+
566
// | Warning | 1292 | Truncated incorrect DECIMAL value: '18446744073709551616' |
567
// +---------+------+-----------------------------------------------------------+
568
// 1 row in set (0.00 sec)
569
570
// table columns:
571
// mysql> select * from t;
572
// +------+------+------+
573
// | f1   | f2   | f3   |
574
// +------+------+------+
575
// |  228 |  184 |  157 |
576
// |  228 |  184 |    0 |
577
// |  228 |  184 |   99 |
578
// |   99 |  228 |  184 |
579
// +------+------+------+
580
// 4 rows in set (0.00 sec)
581
//
582
// mysql> select char(f1, f2, f3 using utf8) from t;
583
// +-----------------------------+
584
// | char(f1, f2, f3 using utf8) |
585
// +-----------------------------+
586
// | 丝                          |
587
// |                             |
588
// |                             |
589
// | c                           |
590
// +-----------------------------+
591
// 4 rows in set, 4 warnings (0.00 sec)
592
//
593
// mysql> show warnings;
594
// +---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
595
// | Level   | Code | Message                                                                                                                                                                     |
596
// +---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
597
// | Warning | 3719 | 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. |
598
// | Warning | 1300 | Invalid utf8mb3 character string: 'E4B800'                                                                                                                                  |
599
// | Warning | 1300 | Invalid utf8mb3 character string: 'E4B863'                                                                                                                                  |
600
// | Warning | 1300 | Invalid utf8mb3 character string: 'E4B8'                                                                                                                                    |
601
// +---------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
602
class FunctionIntToChar : public IFunction {
603
public:
604
    static constexpr auto name = "char";
605
344
    static FunctionPtr create() { return std::make_shared<FunctionIntToChar>(); }
606
0
    String get_name() const override { return name; }
607
0
    size_t get_number_of_arguments() const override { return 0; }
608
336
    bool is_variadic() const override { return true; }
609
610
335
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
611
335
        return make_nullable(std::make_shared<DataTypeString>());
612
335
    }
613
670
    bool use_default_implementation_for_nulls() const override { return false; }
614
615
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
616
335
                        uint32_t result, size_t input_rows_count) const override {
617
335
        DCHECK_GE(arguments.size(), 2);
618
619
335
        int argument_size = arguments.size();
620
335
        std::vector<ColumnPtr> str_columns(argument_size - 1);
621
335
        std::vector<const ColumnString::Offsets*> offsets_list(argument_size - 1);
622
335
        std::vector<const ColumnString::Chars*> chars_list(argument_size - 1);
623
624
        // convert each argument columns to column string and then concat the string columns
625
749
        for (size_t i = 1; i < argument_size; ++i) {
626
414
            if (auto const_column = check_and_get_column<const ColumnConst>(
627
414
                        *block.get_by_position(arguments[i]).column)) {
628
                // ignore null
629
4
                if (const_column->only_null()) {
630
0
                    str_columns[i - 1] = nullptr;
631
4
                } else {
632
4
                    auto str_column = ColumnString::create();
633
4
                    auto& chars = str_column->get_chars();
634
4
                    auto& offsets = str_column->get_offsets();
635
4
                    offsets.resize(1);
636
4
                    const ColumnInt32* int_column;
637
4
                    if (auto* nullable = check_and_get_column<const ColumnNullable>(
638
4
                                const_column->get_data_column())) {
639
0
                        int_column = assert_cast<const ColumnInt32*>(
640
0
                                nullable->get_nested_column_ptr().get());
641
4
                    } else {
642
4
                        int_column =
643
4
                                assert_cast<const ColumnInt32*>(&const_column->get_data_column());
644
4
                    }
645
4
                    int int_val = int_column->get_int(0);
646
4
                    integer_to_char_(0, &int_val, chars, offsets);
647
4
                    str_columns[i - 1] =
648
4
                            ColumnConst::create(std::move(str_column), input_rows_count);
649
4
                }
650
4
                offsets_list[i - 1] = nullptr;
651
4
                chars_list[i - 1] = nullptr;
652
410
            } else {
653
410
                auto str_column = ColumnString::create();
654
410
                auto& chars = str_column->get_chars();
655
410
                auto& offsets = str_column->get_offsets();
656
                // data.resize(input_rows_count);
657
410
                offsets.resize(input_rows_count);
658
659
410
                if (auto nullable = check_and_get_column<const ColumnNullable>(
660
410
                            *block.get_by_position(arguments[i]).column)) {
661
23
                    const auto* int_data =
662
23
                            assert_cast<const ColumnInt32*>(nullable->get_nested_column_ptr().get())
663
23
                                    ->get_data()
664
23
                                    .data();
665
23
                    const auto* null_map_data = nullable->get_null_map_data().data();
666
148
                    for (size_t j = 0; j < input_rows_count; ++j) {
667
                        // ignore null
668
125
                        if (null_map_data[j]) {
669
23
                            offsets[j] = offsets[j - 1];
670
102
                        } else {
671
102
                            integer_to_char_(j, int_data + j, chars, offsets);
672
102
                        }
673
125
                    }
674
387
                } else {
675
387
                    const auto* int_data = assert_cast<const ColumnInt32*>(
676
387
                                                   block.get_by_position(arguments[i]).column.get())
677
387
                                                   ->get_data()
678
387
                                                   .data();
679
818
                    for (size_t j = 0; j < input_rows_count; ++j) {
680
431
                        integer_to_char_(j, int_data + j, chars, offsets);
681
431
                    }
682
387
                }
683
410
                offsets_list[i - 1] = &str_column->get_offsets();
684
410
                chars_list[i - 1] = &str_column->get_chars();
685
410
                str_columns[i - 1] = std::move(str_column);
686
410
            }
687
414
        }
688
689
335
        auto null_map = ColumnUInt8::create(input_rows_count, 0);
690
335
        auto res = ColumnString::create();
691
335
        auto& res_data = res->get_chars();
692
335
        auto& res_offset = res->get_offsets();
693
694
335
        size_t res_reserve_size = 0;
695
749
        for (size_t i = 0; i < argument_size - 1; ++i) {
696
414
            if (!str_columns[i]) {
697
0
                continue;
698
0
            }
699
414
            if (auto const_column = check_and_get_column<const ColumnConst>(*str_columns[i])) {
700
4
                auto str_column =
701
4
                        assert_cast<const ColumnString*>(&(const_column->get_data_column()));
702
4
                auto& offsets = str_column->get_offsets();
703
4
                res_reserve_size += (offsets[0] - offsets[-1]) * input_rows_count;
704
410
            } else {
705
966
                for (size_t j = 0; j < input_rows_count; ++j) {
706
556
                    size_t append = (*offsets_list[i])[j] - (*offsets_list[i])[j - 1];
707
                    // check whether the output might overflow(unlikely)
708
556
                    if (UNLIKELY(UINT_MAX - append < res_reserve_size)) {
709
0
                        return Status::BufferAllocFailed(
710
0
                                "function char output is too large to allocate");
711
0
                    }
712
556
                    res_reserve_size += append;
713
556
                }
714
410
            }
715
414
        }
716
335
        if ((UNLIKELY(UINT_MAX - input_rows_count < res_reserve_size))) {
717
0
            return Status::BufferAllocFailed("function char output is too large to allocate");
718
0
        }
719
335
        ColumnString::check_chars_length(res_reserve_size, 0);
720
335
        res_data.resize(res_reserve_size);
721
335
        res_offset.resize(input_rows_count);
722
723
714
        for (size_t i = 0; i < input_rows_count; ++i) {
724
379
            int current_length = 0;
725
963
            for (size_t j = 0; j < argument_size - 1; ++j) {
726
584
                if (!str_columns[j]) {
727
0
                    continue;
728
0
                }
729
584
                if (auto const_column = check_and_get_column<const ColumnConst>(*str_columns[j])) {
730
28
                    auto str_column = assert_cast<const ColumnString*, TypeCheckOnRelease::DISABLE>(
731
28
                            &(const_column->get_data_column()));
732
28
                    auto data_item = str_column->get_data_at(0);
733
28
                    memcpy_small_allow_read_write_overflow15(
734
28
                            &res_data[res_offset[i - 1]] + current_length, data_item.data,
735
28
                            data_item.size);
736
28
                    current_length += data_item.size;
737
556
                } else {
738
556
                    auto& current_offsets = *offsets_list[j];
739
556
                    auto& current_chars = *chars_list[j];
740
741
556
                    int size = current_offsets[i] - current_offsets[i - 1];
742
556
                    if (size > 0) {
743
533
                        memcpy_small_allow_read_write_overflow15(
744
533
                                &res_data[res_offset[i - 1]] + current_length,
745
533
                                &current_chars[current_offsets[i - 1]], size);
746
533
                        current_length += size;
747
533
                    }
748
556
                }
749
584
            }
750
379
            res_offset[i] = res_offset[i - 1] + current_length;
751
379
        }
752
753
        // validate utf8
754
335
        auto* null_map_data = null_map->get_data().data();
755
714
        for (size_t i = 0; i < input_rows_count; ++i) {
756
379
            if (!validate_utf8((const char*)(&res_data[res_offset[i - 1]]),
757
379
                               res_offset[i] - res_offset[i - 1])) {
758
136
                null_map_data[i] = 1;
759
136
            }
760
379
        }
761
762
335
        block.get_by_position(result).column =
763
335
                ColumnNullable::create(std::move(res), std::move(null_map));
764
335
        return Status::OK();
765
335
    }
766
767
private:
768
    void integer_to_char_(int line_num, const int* num, ColumnString::Chars& chars,
769
537
                          IColumn::Offsets& offsets) const {
770
537
        if (0 == *num) {
771
41
            chars.push_back('\0');
772
41
            offsets[line_num] = offsets[line_num - 1] + 1;
773
41
            return;
774
41
        }
775
496
        const char* bytes = (const char*)(num);
776
496
        if constexpr (std::endian::native == std::endian::little) {
777
496
            int k = 3;
778
1.90k
            for (; k >= 0; --k) {
779
1.90k
                if (bytes[k]) {
780
496
                    break;
781
496
                }
782
1.90k
            }
783
496
            offsets[line_num] = offsets[line_num - 1] + k + 1;
784
1.07k
            for (; k >= 0; --k) {
785
574
                chars.push_back(bytes[k] ? bytes[k] : '\0');
786
574
            }
787
        } else if constexpr (std::endian::native == std::endian::big) {
788
            int k = 0;
789
            for (; k < 4; ++k) {
790
                if (bytes[k]) {
791
                    break;
792
                }
793
            }
794
            offsets[line_num] = offsets[line_num - 1] + 4 - k;
795
            for (; k < 4; ++k) {
796
                chars.push_back(bytes[k] ? bytes[k] : '\0');
797
            }
798
        } else {
799
            static_assert(std::endian::native == std::endian::big ||
800
                                  std::endian::native == std::endian::little,
801
                          "Unsupported endianness");
802
        }
803
496
    }
804
};
805
806
class FunctionNgramSearch : public IFunction {
807
public:
808
    static constexpr auto name = "ngram_search";
809
64
    static FunctionPtr create() { return std::make_shared<FunctionNgramSearch>(); }
810
1
    String get_name() const override { return name; }
811
55
    size_t get_number_of_arguments() const override { return 3; }
812
55
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
813
55
        return std::make_shared<DataTypeFloat64>();
814
55
    }
815
816
    // ngram_search(text,pattern,gram_num)
817
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
818
51
                        uint32_t result, size_t input_rows_count) const override {
819
51
        CHECK_EQ(arguments.size(), 3);
820
51
        auto col_res = ColumnFloat64::create();
821
51
        bool col_const[3];
822
51
        ColumnPtr argument_columns[3];
823
204
        for (int i = 0; i < 3; ++i) {
824
153
            std::tie(argument_columns[i], col_const[i]) =
825
153
                    unpack_if_const(block.get_by_position(arguments[i]).column);
826
153
        }
827
51
        auto pattern = assert_cast<const ColumnString*>(argument_columns[1].get())->get_data_at(0);
828
51
        auto gram_num = assert_cast<const ColumnInt32*>(argument_columns[2].get())->get_element(0);
829
        // FE only rejects a nonpositive gram_num once it is a literal. A constant expression that
830
        // FE cannot evaluate (e.g. `crc32('abc') % 3 - 3`) reaches BE unchecked when the whole
831
        // call is folded on BE, so validate here before it is used as a substring length.
832
51
        if (gram_num <= 0) {
833
16
            return Status::InvalidArgument(
834
16
                    "ngram_search(text,pattern,gram_num): gram_num must be a positive constant.");
835
16
        }
836
35
        const auto* text_col = assert_cast<const ColumnString*>(argument_columns[0].get());
837
838
35
        if (col_const[0]) {
839
0
            _execute_impl<true>(text_col, pattern, gram_num, *col_res, input_rows_count);
840
35
        } else {
841
35
            _execute_impl<false>(text_col, pattern, gram_num, *col_res, input_rows_count);
842
35
        }
843
844
35
        block.replace_by_position(result, std::move(col_res));
845
35
        return Status::OK();
846
51
    }
847
848
private:
849
    using NgramMap = phmap::flat_hash_map<uint32_t, uint8_t>;
850
    constexpr static auto not_found = 0b00;
851
    constexpr static auto found_in_pattern = 0b01;
852
    constexpr static auto found_in_text = 0b10;
853
    constexpr static auto found_in_pattern_and_text = 0b11;
854
855
305
    uint32_t sub_str_hash(const char* data, int32_t length) const {
856
305
        constexpr static uint32_t seed = 0;
857
305
        return crc32c::Extend(seed, (const uint8_t*)data, length);
858
305
    }
859
860
    template <bool column_const>
861
    void _execute_impl(const ColumnString* text_col, StringRef& pattern, int gram_num,
862
35
                       ColumnFloat64& res, size_t size) const {
863
35
        auto& res_data = res.get_data();
864
35
        res_data.resize_fill(size, 0);
865
        // If the length of the pattern is less than gram_num, return 0.
866
35
        if (pattern.size < gram_num) {
867
0
            return;
868
0
        }
869
870
        // Build a map by pattern string, which will be used repeatedly in the following loop.
871
35
        NgramMap pattern_map;
872
35
        int pattern_count = get_pattern_set(pattern_map, pattern, gram_num);
873
        // Each time a loop is executed, the map will be modified, so it needs to be restored afterward.
874
35
        std::vector<uint32_t> restore_map;
875
876
95
        for (int i = 0; i < size; i++) {
877
60
            auto text = text_col->get_data_at(index_check_const<column_const>(i));
878
60
            if (text.size < gram_num) {
879
                // If the length of the text is less than gram_num, return 0.
880
13
                continue;
881
13
            }
882
47
            restore_map.reserve(text.size);
883
47
            auto [text_count, intersection_count] =
884
47
                    get_text_set(text, gram_num, pattern_map, restore_map);
885
886
            // 2 * |Intersection| / (|text substr set| + |pattern substr set|)
887
47
            res_data[i] = 2.0 * intersection_count / (text_count + pattern_count);
888
47
        }
889
35
    }
Unexecuted instantiation: _ZNK5doris19FunctionNgramSearch13_execute_implILb1EEEvPKNS_9ColumnStrIjEERNS_9StringRefEiRNS_12ColumnVectorILNS_13PrimitiveTypeE9EEEm
_ZNK5doris19FunctionNgramSearch13_execute_implILb0EEEvPKNS_9ColumnStrIjEERNS_9StringRefEiRNS_12ColumnVectorILNS_13PrimitiveTypeE9EEEm
Line
Count
Source
862
35
                       ColumnFloat64& res, size_t size) const {
863
35
        auto& res_data = res.get_data();
864
35
        res_data.resize_fill(size, 0);
865
        // If the length of the pattern is less than gram_num, return 0.
866
35
        if (pattern.size < gram_num) {
867
0
            return;
868
0
        }
869
870
        // Build a map by pattern string, which will be used repeatedly in the following loop.
871
35
        NgramMap pattern_map;
872
35
        int pattern_count = get_pattern_set(pattern_map, pattern, gram_num);
873
        // Each time a loop is executed, the map will be modified, so it needs to be restored afterward.
874
35
        std::vector<uint32_t> restore_map;
875
876
95
        for (int i = 0; i < size; i++) {
877
60
            auto text = text_col->get_data_at(index_check_const<column_const>(i));
878
60
            if (text.size < gram_num) {
879
                // If the length of the text is less than gram_num, return 0.
880
13
                continue;
881
13
            }
882
47
            restore_map.reserve(text.size);
883
47
            auto [text_count, intersection_count] =
884
47
                    get_text_set(text, gram_num, pattern_map, restore_map);
885
886
            // 2 * |Intersection| / (|text substr set| + |pattern substr set|)
887
47
            res_data[i] = 2.0 * intersection_count / (text_count + pattern_count);
888
47
        }
889
35
    }
890
891
35
    size_t get_pattern_set(NgramMap& pattern_map, StringRef& pattern, int gram_num) const {
892
35
        size_t pattern_count = 0;
893
143
        for (int i = 0; i + gram_num <= pattern.size; i++) {
894
108
            uint32_t cur_hash = sub_str_hash(pattern.data + i, gram_num);
895
108
            if (!pattern_map.contains(cur_hash)) {
896
78
                pattern_map[cur_hash] = found_in_pattern;
897
78
                pattern_count++;
898
78
            }
899
108
        }
900
35
        return pattern_count;
901
35
    }
902
903
    std::pair<size_t, size_t> get_text_set(StringRef& text, int gram_num, NgramMap& pattern_map,
904
47
                                           std::vector<uint32_t>& restore_map) const {
905
47
        restore_map.clear();
906
        //intersection_count indicates a substring both in pattern and text.
907
47
        size_t text_count = 0, intersection_count = 0;
908
244
        for (int i = 0; i + gram_num <= text.size; i++) {
909
197
            uint32_t cur_hash = sub_str_hash(text.data + i, gram_num);
910
197
            auto& val = pattern_map[cur_hash];
911
197
            if (val == not_found) {
912
42
                val ^= found_in_text;
913
42
                DCHECK(val == found_in_text);
914
                // only found in text
915
42
                text_count++;
916
42
                restore_map.push_back(cur_hash);
917
155
            } else if (val == found_in_pattern) {
918
94
                val ^= found_in_text;
919
94
                DCHECK(val == found_in_pattern_and_text);
920
                // found in text and pattern
921
94
                text_count++;
922
94
                intersection_count++;
923
94
                restore_map.push_back(cur_hash);
924
94
            }
925
197
        }
926
        // Restore the pattern_map.
927
136
        for (auto& restore_hash : restore_map) {
928
136
            pattern_map[restore_hash] ^= found_in_text;
929
136
        }
930
931
47
        return {text_count, intersection_count};
932
47
    }
933
};
934
935
class FunctionTranslate : public IFunction {
936
public:
937
    static constexpr auto name = "translate";
938
    using AsciiMap = std::array<UInt8, 128>;
939
    constexpr static UInt8 DELETE_CHAR = 255; // 255 means delete this char
940
102
    static FunctionPtr create() { return std::make_shared<FunctionTranslate>(); }
941
1
    String get_name() const override { return name; }
942
93
    size_t get_number_of_arguments() const override { return 3; }
943
944
93
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
945
93
        return std::make_shared<DataTypeString>();
946
93
    };
947
948
8
    DataTypes get_variadic_argument_types_impl() const override {
949
8
        return {std::make_shared<DataTypeString>(), std::make_shared<DataTypeString>(),
950
8
                std::make_shared<DataTypeString>()};
951
8
    }
952
953
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
954
150
                        uint32_t result, size_t input_rows_count) const override {
955
150
        CHECK_EQ(arguments.size(), 3);
956
150
        auto col_res = ColumnString::create();
957
150
        bool col_const[3];
958
150
        ColumnPtr argument_columns[3];
959
600
        for (int i = 0; i < 3; ++i) {
960
450
            col_const[i] = is_column_const(*block.get_by_position(arguments[i]).column);
961
450
        }
962
150
        argument_columns[0] = col_const[0] ? static_cast<const ColumnConst&>(
963
20
                                                     *block.get_by_position(arguments[0]).column)
964
20
                                                     .convert_to_full_column()
965
150
                                           : block.get_by_position(arguments[0]).column;
966
150
        default_preprocess_parameter_columns(argument_columns, col_const, {1, 2}, block, arguments);
967
968
150
        const auto* col_source = assert_cast<const ColumnString*>(argument_columns[0].get());
969
150
        const auto* col_from = assert_cast<const ColumnString*>(argument_columns[1].get());
970
150
        const auto* col_to = assert_cast<const ColumnString*>(argument_columns[2].get());
971
972
150
        auto impl_vectors = impl_vectors_utf8<false>;
973
150
        if (col_const[1] && col_const[2]) {
974
            // Non-ASCII mappings require UTF-8 processing regardless of the source.
975
37
            const bool mapping_ascii = col_from->is_ascii() && col_to->is_ascii();
976
37
            if (mapping_ascii) {
977
37
                const bool source_ascii = col_source->is_ascii();
978
37
                impl_vectors = source_ascii ? impl_vectors_ascii<true> : impl_vectors_mixed<true>;
979
37
            } else {
980
0
                impl_vectors = impl_vectors_utf8<true>;
981
0
            }
982
113
        } else {
983
            // A non-ASCII source needs no mapping scans when the mappings vary by row.
984
113
            const bool all_ascii =
985
113
                    col_source->is_ascii() && col_from->is_ascii() && col_to->is_ascii();
986
113
            if (all_ascii) {
987
88
                impl_vectors = impl_vectors_ascii<false>;
988
88
            }
989
113
        }
990
150
        impl_vectors(col_source, col_from, col_to, col_res.get());
991
150
        block.get_by_position(result).column = std::move(col_res);
992
150
        return Status::OK();
993
150
    }
994
995
private:
996
5
    static bool bytes_is_ascii(const char* data, size_t size) {
997
5
        return simd::VStringFunctions::is_ascii({data, size});
998
5
    }
999
1000
    // Per-row dispatch: ASCII source rows use the 128-byte table; only non-ASCII
1001
    // rows fall back to the UTF-8 map. Requires from/to to be ASCII.
1002
    template <bool IsConst>
1003
    static void impl_vectors_mixed(const ColumnString* col_source, const ColumnString* col_from,
1004
3
                                   const ColumnString* col_to, ColumnString* col_res) {
1005
3
        auto& res_chars = col_res->get_chars();
1006
3
        auto& res_offsets = col_res->get_offsets();
1007
3
        res_chars.reserve(col_source->get_chars().size());
1008
3
        res_offsets.reserve(col_source->get_offsets().size());
1009
3
        DCHECK_EQ(col_res->size(), 0);
1010
3
        AsciiMap map;
1011
3
        std::unordered_map<std::string_view, std::string_view> translate_map;
1012
3
        if (IsConst) {
1013
3
            const auto& from_str = col_from->get_data_at(0);
1014
3
            const auto& to_str = col_to->get_data_at(0);
1015
3
            if (!build_translate_map_ascii(map, from_str, to_str)) {
1016
                // No deletes: whole-column table translate; high bytes (>=128) are
1017
                // identity in the map so multi-byte UTF-8 sequences are preserved.
1018
2
                res_offsets.insert(col_source->get_offsets().begin(),
1019
2
                                   col_source->get_offsets().end());
1020
2
                res_chars.insert(col_source->get_chars().begin(), col_source->get_chars().end());
1021
20
                for (size_t i = 0; i < res_chars.size(); ++i) {
1022
18
                    auto c = static_cast<UInt8>(res_chars[i]);
1023
18
                    if (c < 128) {
1024
6
                        res_chars[i] = static_cast<char>(map[c]);
1025
6
                    }
1026
18
                }
1027
2
                return;
1028
2
            }
1029
1
            translate_map =
1030
1
                    build_translate_map_utf8(from_str.to_string_view(), to_str.to_string_view());
1031
1
        }
1032
1033
1
        std::string row_buf;
1034
6
        for (size_t i = 0; i < col_source->size(); ++i) {
1035
5
            const auto& source_str = col_source->get_data_at(i);
1036
5
            if (!IsConst) {
1037
0
                const auto& from_str = col_from->get_data_at(i);
1038
0
                const auto& to_str = col_to->get_data_at(i);
1039
0
                build_translate_map_ascii(map, from_str, to_str);
1040
0
                translate_map = build_translate_map_utf8(from_str.to_string_view(),
1041
0
                                                         to_str.to_string_view());
1042
0
            }
1043
5
            if (bytes_is_ascii(source_str.data, source_str.size)) {
1044
                // ASCII row: O(n) table lookup, no hashing
1045
4
                row_buf.clear();
1046
4
                row_buf.resize(source_str.size);
1047
4
                auto written =
1048
4
                        translate_ascii(source_str, map, reinterpret_cast<UInt8*>(row_buf.data()));
1049
4
                row_buf.resize(written);
1050
4
                col_res->insert_data(row_buf.data(), row_buf.size());
1051
4
            } else {
1052
                // Non-ASCII row: UTF-8 map (built once when IsConst)
1053
1
                auto translated_str = translate_utf8(source_str.to_string_view(), translate_map);
1054
1
                col_res->insert_data(translated_str.data(), translated_str.size());
1055
1
            }
1056
5
        }
1057
1
    }
1058
1059
    template <bool IsConst>
1060
    static void impl_vectors_ascii(const ColumnString* col_source, const ColumnString* col_from,
1061
122
                                   const ColumnString* col_to, ColumnString* col_res) {
1062
122
        auto& res_chars = col_res->get_chars();
1063
122
        auto& res_offsets = col_res->get_offsets();
1064
122
        res_chars.reserve(col_source->get_chars().size());
1065
122
        res_offsets.reserve(col_source->get_offsets().size());
1066
122
        DCHECK_EQ(col_res->size(), 0);
1067
122
        AsciiMap map;
1068
122
        if (IsConst) {
1069
34
            const auto& from_str = col_from->get_data_at(0);
1070
34
            const auto& to_str = col_to->get_data_at(0);
1071
34
            if (!build_translate_map_ascii(map, from_str, to_str)) {
1072
                // if the map is not need delete char, we can directly copy the source string,then use map to translate
1073
24
                res_offsets.insert(col_source->get_offsets().begin(),
1074
24
                                   col_source->get_offsets().end());
1075
24
                res_chars.insert(col_source->get_chars().begin(), col_source->get_chars().end());
1076
214
                for (int i = 0; i < res_chars.size(); ++i) {
1077
190
                    res_chars[i] = map[res_chars[i]]; // translate the chars
1078
190
                }
1079
24
                return; // no need to translate
1080
24
            }
1081
34
        }
1082
1083
98
        auto res_size = 0;
1084
98
        auto* begin_data = col_res->get_chars().data();
1085
216
        for (size_t i = 0; i < col_source->size(); ++i) {
1086
118
            const auto& source_str = col_source->get_data_at(i);
1087
118
            if (!IsConst) {
1088
104
                const auto& from_str = col_from->get_data_at(i);
1089
104
                const auto& to_str = col_to->get_data_at(i);
1090
104
                build_translate_map_ascii(map, from_str, to_str);
1091
104
            }
1092
118
            auto* dst_data = begin_data + res_size;
1093
118
            res_size += translate_ascii(source_str, map, dst_data);
1094
1095
118
            res_offsets.push_back(res_size);
1096
118
        }
1097
98
        DCHECK_GE(res_chars.capacity(), res_size);
1098
98
        res_chars.resize(res_size);
1099
98
    }
_ZN5doris17FunctionTranslate18impl_vectors_asciiILb1EEEvPKNS_9ColumnStrIjEES5_S5_PS3_
Line
Count
Source
1061
34
                                   const ColumnString* col_to, ColumnString* col_res) {
1062
34
        auto& res_chars = col_res->get_chars();
1063
34
        auto& res_offsets = col_res->get_offsets();
1064
34
        res_chars.reserve(col_source->get_chars().size());
1065
34
        res_offsets.reserve(col_source->get_offsets().size());
1066
34
        DCHECK_EQ(col_res->size(), 0);
1067
34
        AsciiMap map;
1068
34
        if (IsConst) {
1069
34
            const auto& from_str = col_from->get_data_at(0);
1070
34
            const auto& to_str = col_to->get_data_at(0);
1071
34
            if (!build_translate_map_ascii(map, from_str, to_str)) {
1072
                // if the map is not need delete char, we can directly copy the source string,then use map to translate
1073
24
                res_offsets.insert(col_source->get_offsets().begin(),
1074
24
                                   col_source->get_offsets().end());
1075
24
                res_chars.insert(col_source->get_chars().begin(), col_source->get_chars().end());
1076
214
                for (int i = 0; i < res_chars.size(); ++i) {
1077
190
                    res_chars[i] = map[res_chars[i]]; // translate the chars
1078
190
                }
1079
24
                return; // no need to translate
1080
24
            }
1081
34
        }
1082
1083
10
        auto res_size = 0;
1084
10
        auto* begin_data = col_res->get_chars().data();
1085
24
        for (size_t i = 0; i < col_source->size(); ++i) {
1086
14
            const auto& source_str = col_source->get_data_at(i);
1087
14
            if (!IsConst) {
1088
0
                const auto& from_str = col_from->get_data_at(i);
1089
0
                const auto& to_str = col_to->get_data_at(i);
1090
0
                build_translate_map_ascii(map, from_str, to_str);
1091
0
            }
1092
14
            auto* dst_data = begin_data + res_size;
1093
14
            res_size += translate_ascii(source_str, map, dst_data);
1094
1095
14
            res_offsets.push_back(res_size);
1096
14
        }
1097
        DCHECK_GE(res_chars.capacity(), res_size);
1098
10
        res_chars.resize(res_size);
1099
10
    }
_ZN5doris17FunctionTranslate18impl_vectors_asciiILb0EEEvPKNS_9ColumnStrIjEES5_S5_PS3_
Line
Count
Source
1061
88
                                   const ColumnString* col_to, ColumnString* col_res) {
1062
88
        auto& res_chars = col_res->get_chars();
1063
88
        auto& res_offsets = col_res->get_offsets();
1064
88
        res_chars.reserve(col_source->get_chars().size());
1065
88
        res_offsets.reserve(col_source->get_offsets().size());
1066
88
        DCHECK_EQ(col_res->size(), 0);
1067
88
        AsciiMap map;
1068
88
        if (IsConst) {
1069
0
            const auto& from_str = col_from->get_data_at(0);
1070
0
            const auto& to_str = col_to->get_data_at(0);
1071
0
            if (!build_translate_map_ascii(map, from_str, to_str)) {
1072
                // if the map is not need delete char, we can directly copy the source string,then use map to translate
1073
0
                res_offsets.insert(col_source->get_offsets().begin(),
1074
0
                                   col_source->get_offsets().end());
1075
0
                res_chars.insert(col_source->get_chars().begin(), col_source->get_chars().end());
1076
0
                for (int i = 0; i < res_chars.size(); ++i) {
1077
0
                    res_chars[i] = map[res_chars[i]]; // translate the chars
1078
0
                }
1079
0
                return; // no need to translate
1080
0
            }
1081
0
        }
1082
1083
88
        auto res_size = 0;
1084
88
        auto* begin_data = col_res->get_chars().data();
1085
192
        for (size_t i = 0; i < col_source->size(); ++i) {
1086
104
            const auto& source_str = col_source->get_data_at(i);
1087
104
            if (!IsConst) {
1088
104
                const auto& from_str = col_from->get_data_at(i);
1089
104
                const auto& to_str = col_to->get_data_at(i);
1090
104
                build_translate_map_ascii(map, from_str, to_str);
1091
104
            }
1092
104
            auto* dst_data = begin_data + res_size;
1093
104
            res_size += translate_ascii(source_str, map, dst_data);
1094
1095
104
            res_offsets.push_back(res_size);
1096
104
        }
1097
        DCHECK_GE(res_chars.capacity(), res_size);
1098
88
        res_chars.resize(res_size);
1099
88
    }
1100
1101
    // return true if no need delete char
1102
    bool static build_translate_map_ascii(AsciiMap& map, const StringRef& from_str,
1103
141
                                          const StringRef& to_str) {
1104
18.1k
        for (size_t i = 0; i < map.size(); ++i) {
1105
18.0k
            map[i] = i; // initialize map to identity
1106
18.0k
        }
1107
141
        std::array<UInt8, 128> set_map {0};
1108
141
        const auto min_size = std::min(from_str.size, to_str.size);
1109
        // all ascii characters are in the range [0, 127]
1110
485
        for (size_t i = 0; i < min_size; ++i) {
1111
344
            auto from_char = from_str.data[i];
1112
344
            auto to_char = to_str.data[i];
1113
344
            if (set_map[from_char] == 0) {
1114
249
                set_map[from_char] = 1;
1115
249
                map[from_char] = to_char;
1116
249
            }
1117
344
        }
1118
1119
141
        bool need_delete_char = false;
1120
1121
211
        for (size_t i = min_size; i < from_str.size; ++i) {
1122
70
            auto from_char = from_str.data[i];
1123
70
            if (set_map[from_char] == 0) {
1124
58
                set_map[from_char] = 1;
1125
58
                map[from_char] = DELETE_CHAR; // delete this char
1126
58
                need_delete_char = true;
1127
58
            }
1128
70
        }
1129
141
        return need_delete_char;
1130
141
    }
1131
1132
122
    static size_t translate_ascii(const StringRef& source_str, AsciiMap& map, UInt8* dst_data) {
1133
122
        auto* begin_data = dst_data;
1134
650
        for (size_t i = 0; i < source_str.size; ++i) {
1135
528
            auto c = source_str.data[i];
1136
528
            if (map[c] == DELETE_CHAR) {
1137
39
                continue; // delete this char
1138
39
            }
1139
489
            *dst_data++ = map[c];
1140
489
        }
1141
122
        return dst_data - begin_data;
1142
122
    }
1143
1144
    template <bool IsConst>
1145
    static void impl_vectors_utf8(const ColumnString* col_source, const ColumnString* col_from,
1146
25
                                  const ColumnString* col_to, ColumnString* col_res) {
1147
25
        col_res->get_chars().reserve(col_source->get_chars().size());
1148
25
        col_res->get_offsets().reserve(col_source->get_offsets().size());
1149
25
        std::unordered_map<std::string_view, std::string_view> translate_map;
1150
25
        if (IsConst) {
1151
0
            const auto& from_str = col_from->get_data_at(0);
1152
0
            const auto& to_str = col_to->get_data_at(0);
1153
0
            translate_map =
1154
0
                    build_translate_map_utf8(from_str.to_string_view(), to_str.to_string_view());
1155
0
        }
1156
392
        for (size_t i = 0; i < col_source->size(); ++i) {
1157
367
            const auto& source_str = col_source->get_data_at(i);
1158
367
            if (!IsConst) {
1159
367
                const auto& from_str = col_from->get_data_at(i);
1160
367
                const auto& to_str = col_to->get_data_at(i);
1161
367
                translate_map = build_translate_map_utf8(from_str.to_string_view(),
1162
367
                                                         to_str.to_string_view());
1163
367
            }
1164
367
            auto translated_str = translate_utf8(source_str.to_string_view(), translate_map);
1165
367
            col_res->insert_data(translated_str.data(), translated_str.size());
1166
367
        }
1167
25
    }
_ZN5doris17FunctionTranslate17impl_vectors_utf8ILb0EEEvPKNS_9ColumnStrIjEES5_S5_PS3_
Line
Count
Source
1146
25
                                  const ColumnString* col_to, ColumnString* col_res) {
1147
25
        col_res->get_chars().reserve(col_source->get_chars().size());
1148
25
        col_res->get_offsets().reserve(col_source->get_offsets().size());
1149
25
        std::unordered_map<std::string_view, std::string_view> translate_map;
1150
25
        if (IsConst) {
1151
0
            const auto& from_str = col_from->get_data_at(0);
1152
0
            const auto& to_str = col_to->get_data_at(0);
1153
0
            translate_map =
1154
0
                    build_translate_map_utf8(from_str.to_string_view(), to_str.to_string_view());
1155
0
        }
1156
392
        for (size_t i = 0; i < col_source->size(); ++i) {
1157
367
            const auto& source_str = col_source->get_data_at(i);
1158
367
            if (!IsConst) {
1159
367
                const auto& from_str = col_from->get_data_at(i);
1160
367
                const auto& to_str = col_to->get_data_at(i);
1161
367
                translate_map = build_translate_map_utf8(from_str.to_string_view(),
1162
367
                                                         to_str.to_string_view());
1163
367
            }
1164
367
            auto translated_str = translate_utf8(source_str.to_string_view(), translate_map);
1165
367
            col_res->insert_data(translated_str.data(), translated_str.size());
1166
367
        }
1167
25
    }
Unexecuted instantiation: _ZN5doris17FunctionTranslate17impl_vectors_utf8ILb1EEEvPKNS_9ColumnStrIjEES5_S5_PS3_
1168
1169
    static std::unordered_map<std::string_view, std::string_view> build_translate_map_utf8(
1170
368
            const std::string_view& from_str, const std::string_view& to_str) {
1171
368
        std::unordered_map<std::string_view, std::string_view> translate_map;
1172
1.98k
        for (size_t i = 0, from_char_size = 0, j = 0, to_char_size = 0; i < from_str.size();
1173
1.61k
             i += from_char_size, j += to_char_size) {
1174
1.61k
            from_char_size = get_utf8_byte_length(from_str[i]);
1175
1.61k
            to_char_size = j < to_str.size() ? get_utf8_byte_length(to_str[j]) : 0;
1176
1.61k
            auto from_char = from_str.substr(i, from_char_size);
1177
1.61k
            if (translate_map.find(from_char) == translate_map.end()) {
1178
875
                translate_map[from_char] =
1179
875
                        j < to_str.size() ? to_str.substr(j, to_char_size) : std::string_view();
1180
875
            }
1181
1.61k
        }
1182
368
        return translate_map;
1183
368
    }
1184
1185
    static std::string translate_utf8(
1186
            const std::string_view& source_str,
1187
368
            std::unordered_map<std::string_view, std::string_view>& translate_map) {
1188
368
        std::string result;
1189
368
        result.reserve(source_str.size());
1190
2.02k
        for (size_t i = 0, char_size = 0; i < source_str.size(); i += char_size) {
1191
1.65k
            char_size = get_utf8_byte_length(source_str[i]);
1192
1.65k
            auto c = source_str.substr(i, char_size);
1193
1.65k
            auto it = translate_map.find(c);
1194
1.65k
            if (it != translate_map.end()) {
1195
258
                if (!it->second.empty()) {
1196
161
                    result.append(it->second);
1197
161
                }
1198
1.39k
            } else {
1199
1.39k
                result.append(c);
1200
1.39k
            }
1201
1.65k
        }
1202
368
        return result;
1203
368
    }
1204
};
1205
1206
/// xpath_string(xml, xpath) -> String
1207
/// Returns the text content of the first node that matches the XPath expression.
1208
/// Returns NULL if either xml or xpath is NULL.
1209
/// Returns empty string if the XPath expression matches no nodes.
1210
/// The text content includes the node and all its descendants.
1211
/// Example:
1212
///   xpath_string('<a><b>b1</b><b>b2</b></a>', '/a/b[1]') = 'b1'
1213
///   xpath_string('<a><b>b1</b><b>b2</b></a>', '/a/b[2]') = 'b2'
1214
///   xpath_string('<a><b>b1</b><b>b2</b></a>', '/a/c') = ''
1215
///   xpath_string('invalid xml', '/a/b[1]') = NULL
1216
///   xpath_string(NULL, '/a/b[1]') = NULL
1217
///   xpath_string('<a><b>b1</b><b>b2</b></a>', NULL) = NULL
1218
class FunctionXPathString : public IFunction {
1219
public:
1220
    static constexpr auto name = "xpath_string";
1221
173
    static FunctionPtr create() { return std::make_shared<FunctionXPathString>(); }
1222
1
    String get_name() const override { return name; }
1223
164
    size_t get_number_of_arguments() const override { return 2; }
1224
164
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
1225
164
        return make_nullable(std::make_shared<DataTypeString>());
1226
164
    }
1227
1228
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
1229
246
                        uint32_t result, size_t input_rows_count) const override {
1230
246
        CHECK_EQ(arguments.size(), 2);
1231
246
        auto col_res = ColumnNullable::create(ColumnString::create(), ColumnUInt8::create());
1232
246
        const auto& [left_col, left_const] =
1233
246
                unpack_if_const(block.get_by_position(arguments[0]).column);
1234
246
        const auto& [right_col, right_const] =
1235
246
                unpack_if_const(block.get_by_position(arguments[1]).column);
1236
246
        const auto& xml_col = *assert_cast<const ColumnString*>(left_col.get());
1237
246
        const auto& xpath_col = *assert_cast<const ColumnString*>(right_col.get());
1238
1239
246
        Status status;
1240
246
        if (left_const && right_const) {
1241
0
            status = execute_vector<true, true>(input_rows_count, xml_col, xpath_col, *col_res);
1242
246
        } else if (left_const) {
1243
42
            status = execute_vector<true, false>(input_rows_count, xml_col, xpath_col, *col_res);
1244
204
        } else if (right_const) {
1245
51
            status = execute_vector<false, true>(input_rows_count, xml_col, xpath_col, *col_res);
1246
153
        } else {
1247
153
            status = execute_vector<false, false>(input_rows_count, xml_col, xpath_col, *col_res);
1248
153
        }
1249
246
        if (!status.ok()) {
1250
1
            return status;
1251
1
        }
1252
1253
245
        block.get_by_position(result).column = std::move(col_res);
1254
245
        return Status::OK();
1255
246
    }
1256
1257
private:
1258
331
    static Status parse_xml(const StringRef& xml_str, pugi::xml_document& xml_doc) {
1259
331
        pugi::xml_parse_result result = xml_doc.load_buffer(xml_str.data, xml_str.size);
1260
331
        if (!result) {
1261
1
            return Status::InvalidArgument("Function {} failed to parse XML string: {}", name,
1262
1
                                           result.description());
1263
1
        }
1264
330
        return Status::OK();
1265
331
    }
1266
1267
340
    static Status build_xpath_query(const StringRef& xpath_str, pugi::xpath_query& xpath_query) {
1268
        // xpath_query will throws xpath_exception on compilation errors.
1269
340
        try {
1270
            // NOTE!!!: don't use to_string_view(), because xpath_str maybe not null-terminated
1271
340
            xpath_query = pugi::xpath_query(xpath_str.to_string().c_str());
1272
340
        } catch (const pugi::xpath_exception& e) {
1273
0
            return Status::InvalidArgument("Function {} failed to build XPath query: {}", name,
1274
0
                                           e.what());
1275
0
        }
1276
340
        return Status::OK();
1277
340
    }
1278
1279
    template <bool left_const, bool right_const>
1280
    static Status execute_vector(const size_t input_rows_count, const ColumnString& xml_col,
1281
246
                                 const ColumnString& xpath_col, ColumnNullable& res_col) {
1282
246
        pugi::xml_document xml_doc;
1283
246
        pugi::xpath_query xpath_query;
1284
        // first check right_const, because we want to check empty input first
1285
246
        if constexpr (right_const) {
1286
51
            auto xpath_str = xpath_col.get_data_at(0);
1287
51
            if (xpath_str.empty()) {
1288
                // should return null if xpath_str is empty
1289
1
                res_col.insert_many_defaults(input_rows_count);
1290
1
                return Status::OK();
1291
1
            }
1292
50
            RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1293
50
        }
1294
50
        if constexpr (left_const) {
1295
42
            auto xml_str = xml_col.get_data_at(0);
1296
42
            if (xml_str.empty()) {
1297
                // should return null if xml_str is empty
1298
1
                res_col.insert_many_defaults(input_rows_count);
1299
1
                return Status::OK();
1300
1
            }
1301
41
            RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1302
41
        }
1303
1304
633
        for (size_t i = 0; i < input_rows_count; ++i) {
1305
388
            if constexpr (!right_const) {
1306
308
                auto xpath_str = xpath_col.get_data_at(i);
1307
308
                if (xpath_str.empty()) {
1308
                    // should return null if xpath_str is empty
1309
18
                    res_col.insert_default();
1310
18
                    continue;
1311
18
                }
1312
290
                RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1313
290
            }
1314
327
            if constexpr (!left_const) {
1315
327
                auto xml_str = xml_col.get_data_at(i);
1316
327
                if (xml_str.empty()) {
1317
                    // should return null if xml_str is empty
1318
20
                    res_col.insert_default();
1319
20
                    continue;
1320
20
                }
1321
307
                RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1322
307
            }
1323
306
            std::string text;
1324
388
            try {
1325
388
                text = xpath_query.evaluate_string(xml_doc);
1326
388
            } catch (const pugi::xpath_exception& e) {
1327
0
                return Status::InvalidArgument("Function {} failed to query XPath string: {}", name,
1328
0
                                               e.what());
1329
0
            }
1330
349
            res_col.insert_data(text.data(), text.size());
1331
349
        }
1332
245
        return Status::OK();
1333
246
    }
Unexecuted instantiation: _ZN5doris19FunctionXPathString14execute_vectorILb1ELb1EEENS_6StatusEmRKNS_9ColumnStrIjEES6_RNS_14ColumnNullableE
_ZN5doris19FunctionXPathString14execute_vectorILb1ELb0EEENS_6StatusEmRKNS_9ColumnStrIjEES6_RNS_14ColumnNullableE
Line
Count
Source
1281
42
                                 const ColumnString& xpath_col, ColumnNullable& res_col) {
1282
42
        pugi::xml_document xml_doc;
1283
42
        pugi::xpath_query xpath_query;
1284
        // first check right_const, because we want to check empty input first
1285
        if constexpr (right_const) {
1286
            auto xpath_str = xpath_col.get_data_at(0);
1287
            if (xpath_str.empty()) {
1288
                // should return null if xpath_str is empty
1289
                res_col.insert_many_defaults(input_rows_count);
1290
                return Status::OK();
1291
            }
1292
            RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1293
        }
1294
42
        if constexpr (left_const) {
1295
42
            auto xml_str = xml_col.get_data_at(0);
1296
42
            if (xml_str.empty()) {
1297
                // should return null if xml_str is empty
1298
1
                res_col.insert_many_defaults(input_rows_count);
1299
1
                return Status::OK();
1300
1
            }
1301
41
            RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1302
41
        }
1303
1304
103
        for (size_t i = 0; i < input_rows_count; ++i) {
1305
61
            if constexpr (!right_const) {
1306
61
                auto xpath_str = xpath_col.get_data_at(i);
1307
61
                if (xpath_str.empty()) {
1308
                    // should return null if xpath_str is empty
1309
1
                    res_col.insert_default();
1310
1
                    continue;
1311
1
                }
1312
60
                RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1313
60
            }
1314
            if constexpr (!left_const) {
1315
                auto xml_str = xml_col.get_data_at(i);
1316
                if (xml_str.empty()) {
1317
                    // should return null if xml_str is empty
1318
                    res_col.insert_default();
1319
                    continue;
1320
                }
1321
                RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1322
            }
1323
61
            std::string text;
1324
61
            try {
1325
61
                text = xpath_query.evaluate_string(xml_doc);
1326
61
            } catch (const pugi::xpath_exception& e) {
1327
0
                return Status::InvalidArgument("Function {} failed to query XPath string: {}", name,
1328
0
                                               e.what());
1329
0
            }
1330
60
            res_col.insert_data(text.data(), text.size());
1331
60
        }
1332
42
        return Status::OK();
1333
42
    }
_ZN5doris19FunctionXPathString14execute_vectorILb0ELb1EEENS_6StatusEmRKNS_9ColumnStrIjEES6_RNS_14ColumnNullableE
Line
Count
Source
1281
51
                                 const ColumnString& xpath_col, ColumnNullable& res_col) {
1282
51
        pugi::xml_document xml_doc;
1283
51
        pugi::xpath_query xpath_query;
1284
        // first check right_const, because we want to check empty input first
1285
51
        if constexpr (right_const) {
1286
51
            auto xpath_str = xpath_col.get_data_at(0);
1287
51
            if (xpath_str.empty()) {
1288
                // should return null if xpath_str is empty
1289
1
                res_col.insert_many_defaults(input_rows_count);
1290
1
                return Status::OK();
1291
1
            }
1292
50
            RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1293
50
        }
1294
        if constexpr (left_const) {
1295
            auto xml_str = xml_col.get_data_at(0);
1296
            if (xml_str.empty()) {
1297
                // should return null if xml_str is empty
1298
                res_col.insert_many_defaults(input_rows_count);
1299
                return Status::OK();
1300
            }
1301
            RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1302
        }
1303
1304
131
        for (size_t i = 0; i < input_rows_count; ++i) {
1305
            if constexpr (!right_const) {
1306
                auto xpath_str = xpath_col.get_data_at(i);
1307
                if (xpath_str.empty()) {
1308
                    // should return null if xpath_str is empty
1309
                    res_col.insert_default();
1310
                    continue;
1311
                }
1312
                RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1313
            }
1314
80
            if constexpr (!left_const) {
1315
80
                auto xml_str = xml_col.get_data_at(i);
1316
80
                if (xml_str.empty()) {
1317
                    // should return null if xml_str is empty
1318
5
                    res_col.insert_default();
1319
5
                    continue;
1320
5
                }
1321
75
                RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1322
75
            }
1323
75
            std::string text;
1324
80
            try {
1325
80
                text = xpath_query.evaluate_string(xml_doc);
1326
80
            } catch (const pugi::xpath_exception& e) {
1327
0
                return Status::InvalidArgument("Function {} failed to query XPath string: {}", name,
1328
0
                                               e.what());
1329
0
            }
1330
75
            res_col.insert_data(text.data(), text.size());
1331
75
        }
1332
51
        return Status::OK();
1333
51
    }
_ZN5doris19FunctionXPathString14execute_vectorILb0ELb0EEENS_6StatusEmRKNS_9ColumnStrIjEES6_RNS_14ColumnNullableE
Line
Count
Source
1281
153
                                 const ColumnString& xpath_col, ColumnNullable& res_col) {
1282
153
        pugi::xml_document xml_doc;
1283
153
        pugi::xpath_query xpath_query;
1284
        // first check right_const, because we want to check empty input first
1285
        if constexpr (right_const) {
1286
            auto xpath_str = xpath_col.get_data_at(0);
1287
            if (xpath_str.empty()) {
1288
                // should return null if xpath_str is empty
1289
                res_col.insert_many_defaults(input_rows_count);
1290
                return Status::OK();
1291
            }
1292
            RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1293
        }
1294
        if constexpr (left_const) {
1295
            auto xml_str = xml_col.get_data_at(0);
1296
            if (xml_str.empty()) {
1297
                // should return null if xml_str is empty
1298
                res_col.insert_many_defaults(input_rows_count);
1299
                return Status::OK();
1300
            }
1301
            RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1302
        }
1303
1304
399
        for (size_t i = 0; i < input_rows_count; ++i) {
1305
247
            if constexpr (!right_const) {
1306
247
                auto xpath_str = xpath_col.get_data_at(i);
1307
247
                if (xpath_str.empty()) {
1308
                    // should return null if xpath_str is empty
1309
17
                    res_col.insert_default();
1310
17
                    continue;
1311
17
                }
1312
230
                RETURN_IF_ERROR(build_xpath_query(xpath_str, xpath_query));
1313
230
            }
1314
247
            if constexpr (!left_const) {
1315
247
                auto xml_str = xml_col.get_data_at(i);
1316
247
                if (xml_str.empty()) {
1317
                    // should return null if xml_str is empty
1318
15
                    res_col.insert_default();
1319
15
                    continue;
1320
15
                }
1321
232
                RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
1322
232
            }
1323
231
            std::string text;
1324
247
            try {
1325
247
                text = xpath_query.evaluate_string(xml_doc);
1326
247
            } catch (const pugi::xpath_exception& e) {
1327
0
                return Status::InvalidArgument("Function {} failed to query XPath string: {}", name,
1328
0
                                               e.what());
1329
0
            }
1330
214
            res_col.insert_data(text.data(), text.size());
1331
214
        }
1332
152
        return Status::OK();
1333
153
    }
1334
};
1335
1336
class MakeSetImpl {
1337
public:
1338
    static constexpr auto name = "make_set";
1339
1340
0
    static size_t get_number_of_arguments() { return 0; }
1341
43
    static bool is_variadic() { return true; }
1342
42
    static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
1343
42
        if (arguments[0].get()->is_nullable()) {
1344
15
            return make_nullable(std::make_shared<DataTypeString>());
1345
15
        }
1346
27
        return std::make_shared<DataTypeString>();
1347
42
    }
1348
1349
    static bool is_return_nullable(bool has_nullable,
1350
42
                                   const std::vector<ColumnWithConstAndNullMap>& cols_info) {
1351
42
        return cols_info[0].null_map != nullptr;
1352
42
    }
1353
1354
    static bool execute_const_null(ColumnString::MutablePtr& res_col,
1355
                                   PaddedPODArray<UInt8>& res_null_map_data,
1356
8
                                   size_t input_rows_count, size_t null_index) {
1357
        // Only a NULL bits argument makes the result NULL; NULL strings are skipped.
1358
8
        if (null_index == 0) {
1359
0
            res_col->insert_many_defaults(input_rows_count);
1360
0
            res_null_map_data.assign(input_rows_count, (UInt8)1);
1361
0
            return true;
1362
0
        }
1363
8
        return false;
1364
8
    }
1365
1366
    static void execute(const std::vector<ColumnWithConstAndNullMap>& column_infos,
1367
                        ColumnString::MutablePtr& res_col, PaddedPODArray<UInt8>& res_null_map_data,
1368
42
                        size_t input_rows_count) {
1369
42
        static constexpr char SEPARATOR = ',';
1370
42
        const auto& bit_data =
1371
42
                assert_cast<const ColumnInt64&>(*column_infos[0].nested_col).get_data();
1372
42
        std::vector<const ColumnString*> str_cols(column_infos.size());
1373
302
        for (size_t i = 1; i < column_infos.size(); ++i) {
1374
260
            str_cols[i] = assert_cast<const ColumnString*>(column_infos[i].nested_col);
1375
260
        }
1376
1377
214
        for (size_t row = 0; row < input_rows_count; ++row) {
1378
172
            if (column_infos[0].is_null_at(row)) {
1379
11
                res_col->insert_default();
1380
11
                res_null_map_data[row] = 1;
1381
11
                continue;
1382
11
            }
1383
1384
161
            uint64_t bit = bit_data[column_infos[0].is_const ? 0 : row];
1385
161
            uint64_t col_pos = __builtin_ffsll(bit);
1386
161
            ColumnString::Chars data;
1387
463
            while (col_pos != 0 && col_pos < column_infos.size() && bit != 0) {
1388
302
                if (!column_infos[col_pos].is_null_at(row)) {
1389
                    /* Here insert `str,` directly to support the case below:
1390
                     * SELECT MAKE_SET(3, '', 'a');
1391
                     * the exception result should be ',a'.
1392
                     */
1393
263
                    auto s_ref = str_cols[col_pos]->get_data_at(
1394
263
                            column_infos[col_pos].is_const ? 0 : row);
1395
263
                    data.insert(s_ref.data, s_ref.data + s_ref.size);
1396
263
                    data.push_back(SEPARATOR);
1397
263
                }
1398
302
                bit &= ~(1ULL << (col_pos - 1));
1399
302
                col_pos = __builtin_ffsll(bit);
1400
302
            }
1401
            // remove the last ','
1402
161
            if (!data.empty()) {
1403
144
                data.pop_back();
1404
144
            }
1405
161
            res_col->insert_data(reinterpret_cast<const char*>(data.data()), data.size());
1406
161
        }
1407
42
    }
1408
};
1409
1410
class FunctionExportSet : public IFunction {
1411
public:
1412
    static constexpr auto name = "export_set";
1413
78
    static FunctionPtr create() { return std::make_shared<FunctionExportSet>(); }
1414
0
    String get_name() const override { return name; }
1415
0
    size_t get_number_of_arguments() const override { return 0; }
1416
70
    bool is_variadic() const override { return true; }
1417
69
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
1418
69
        return std::make_shared<DataTypeString>();
1419
69
    }
1420
1421
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
1422
69
                        uint32_t result, size_t input_rows_count) const override {
1423
69
        auto res_col = ColumnString::create();
1424
1425
69
        const size_t arg_size = arguments.size();
1426
69
        bool col_const[5];
1427
69
        ColumnPtr arg_cols[5];
1428
69
        bool all_const = true;
1429
244
        for (int i = 1; i < arg_size; ++i) {
1430
175
            col_const[i] = is_column_const(*block.get_by_position(arguments[i]).column);
1431
175
            all_const = all_const && col_const[i];
1432
175
        }
1433
69
        std::tie(arg_cols[0], col_const[0]) =
1434
69
                unpack_if_const(block.get_by_position(arguments[0]).column);
1435
69
        if (arg_size == 3) {
1436
49
            default_preprocess_parameter_columns(arg_cols, col_const, {1, 2}, block, arguments);
1437
49
        } else if (arg_size == 4) {
1438
3
            default_preprocess_parameter_columns(arg_cols, col_const, {1, 2, 3}, block, arguments);
1439
17
        } else if (arg_size == 5) {
1440
17
            default_preprocess_parameter_columns(arg_cols, col_const, {1, 2, 3, 4}, block,
1441
17
                                                 arguments);
1442
17
        }
1443
1444
69
        const auto* bit_col = assert_cast<const ColumnInt128*>(arg_cols[0].get());
1445
69
        const auto* on_col = assert_cast<const ColumnString*>(arg_cols[1].get());
1446
69
        const auto* off_col = assert_cast<const ColumnString*>(arg_cols[2].get());
1447
69
        const ColumnString* sep_col = nullptr;
1448
69
        const ColumnInt32* num_bits_col = nullptr;
1449
69
        if (arg_size > 3) {
1450
20
            sep_col = assert_cast<const ColumnString*>(arg_cols[3].get());
1451
20
            if (arg_size == 5) {
1452
17
                num_bits_col = assert_cast<const ColumnInt32*>(arg_cols[4].get());
1453
17
            }
1454
20
        }
1455
1456
246
        for (size_t i = 0; i < input_rows_count; ++i) {
1457
177
            uint64_t bit =
1458
177
                    check_and_get_bit(bit_col->get_element(index_check_const(i, col_const[0])));
1459
1460
177
            size_t idx_for_args = all_const ? 0 : i;
1461
177
            StringRef on = on_col->get_data_at(idx_for_args);
1462
177
            StringRef off = off_col->get_data_at(idx_for_args);
1463
177
            StringRef separator(",", 1);
1464
177
            int8_t num_of_bits = 64;
1465
1466
177
            if (arg_size > 3) {
1467
104
                separator = sep_col->get_data_at(idx_for_args);
1468
104
                if (arg_size == 5) {
1469
77
                    num_of_bits =
1470
77
                            check_and_get_num_of_bits(num_bits_col->get_element(idx_for_args));
1471
77
                }
1472
104
            }
1473
1474
177
            execute_single(bit, on, off, separator, num_of_bits, *res_col);
1475
177
        }
1476
69
        block.replace_by_position(result, std::move(res_col));
1477
69
        return Status::OK();
1478
69
    }
1479
1480
private:
1481
    /* The valid range of the input `bit` parameter should be [-2^63, 2^64 - 1]
1482
     * If it exceeds this range, the MAX/MIN values of the signed 64-bit integer are used for calculation
1483
     * This behavior is consistent with MySQL.
1484
     */
1485
177
    uint64_t check_and_get_bit(__int128 col_bit_val) const {
1486
177
        if (col_bit_val > ULLONG_MAX) {
1487
3
            return LLONG_MAX;
1488
174
        } else if (col_bit_val < LLONG_MIN) {
1489
1
            return LLONG_MIN;
1490
1
        }
1491
173
        return static_cast<uint64_t>(col_bit_val);
1492
177
    }
1493
1494
    // If the input value is not in the range [0, 64], return default value 64
1495
77
    int8_t check_and_get_num_of_bits(int32_t col_num_of_bits_val) const {
1496
77
        if (col_num_of_bits_val >= 0 && col_num_of_bits_val <= 64) {
1497
71
            return static_cast<int8_t>(col_num_of_bits_val);
1498
71
        }
1499
6
        return 64;
1500
77
    }
1501
1502
    void execute_single(uint64_t bit, const StringRef& on, const StringRef& off,
1503
                        const StringRef& separator, int8_t num_of_bits,
1504
177
                        ColumnString& res_col) const {
1505
177
        ColumnString::Chars data;
1506
177
        data.reserve(std::max(on.size, off.size) * num_of_bits +
1507
177
                     separator.size * (num_of_bits - 1));
1508
1509
5.03k
        while (bit && num_of_bits) {
1510
4.86k
            if (bit & 1) {
1511
3.04k
                data.insert(on.data, on.data + on.size);
1512
3.04k
            } else {
1513
1.82k
                data.insert(off.data, off.data + off.size);
1514
1.82k
            }
1515
4.86k
            bit >>= 1;
1516
4.86k
            if (--num_of_bits) {
1517
4.79k
                data.insert(separator.data, separator.data + separator.size);
1518
4.79k
            }
1519
4.86k
        }
1520
1521
177
        if (num_of_bits > 0) {
1522
111
            ColumnString::Chars off_sep_combo;
1523
111
            off_sep_combo.reserve(separator.size + off.size);
1524
111
            off_sep_combo.insert(off_sep_combo.end(), off.data, off.data + off.size);
1525
111
            off_sep_combo.insert(off_sep_combo.end(), separator.data,
1526
111
                                 separator.data + separator.size);
1527
1528
3.30k
            for (size_t i = 0; i < num_of_bits; ++i) {
1529
3.19k
                data.insert(off_sep_combo.data(), off_sep_combo.data() + off_sep_combo.size());
1530
3.19k
            }
1531
111
            data.erase(data.end() - separator.size, data.end());
1532
111
        }
1533
1534
177
        res_col.insert_data(reinterpret_cast<const char*>(data.data()), data.size());
1535
177
    }
1536
};
1537
1538
// ATTN: for debug only
1539
// compute crc32 hash value as the same way in `VOlapTablePartitionParam::find_tablets()`
1540
class FunctionCrc32Internal : public IFunction {
1541
public:
1542
    static constexpr auto name = "crc32_internal";
1543
45.6k
    static FunctionPtr create() { return std::make_shared<FunctionCrc32Internal>(); }
1544
0
    String get_name() const override { return name; }
1545
0
    size_t get_number_of_arguments() const override { return 0; }
1546
45.6k
    bool is_variadic() const override { return true; }
1547
61.9k
    bool use_default_implementation_for_nulls() const override { return false; }
1548
45.5k
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
1549
45.5k
        return std::make_shared<DataTypeInt64>();
1550
45.5k
    }
1551
1552
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
1553
16.3k
                        uint32_t result, size_t input_rows_count) const override {
1554
16.3k
        DCHECK_GE(arguments.size(), 1);
1555
1556
16.3k
        auto argument_size = arguments.size();
1557
16.3k
        std::vector<ColumnPtr> argument_columns(argument_size);
1558
16.3k
        std::vector<PrimitiveType> argument_primitive_types(argument_size);
1559
1560
32.9k
        for (size_t i = 0; i < argument_size; ++i) {
1561
16.6k
            argument_columns[i] =
1562
16.6k
                    block.get_by_position(arguments[i]).column->convert_to_full_column_if_const();
1563
16.6k
            argument_primitive_types[i] =
1564
16.6k
                    block.get_by_position(arguments[i]).type->get_primitive_type();
1565
16.6k
        }
1566
1567
16.3k
        auto res_col = ColumnInt64::create();
1568
16.3k
        auto& res_data = res_col->get_data();
1569
16.3k
        res_data.resize_fill(input_rows_count, 0);
1570
1571
15.8M
        for (size_t i = 0; i < input_rows_count; ++i) {
1572
15.8M
            uint32_t hash_val = 0;
1573
31.6M
            for (size_t j = 0; j < argument_size; ++j) {
1574
15.8M
                const auto& column = argument_columns[j];
1575
15.8M
                auto primitive_type = argument_primitive_types[j];
1576
15.8M
                auto val = column->get_data_at(i);
1577
15.8M
                if (val.data != nullptr) {
1578
15.7M
                    hash_val = RawValue::zlib_crc32(val.data, val.size, primitive_type, hash_val);
1579
15.7M
                } else {
1580
83.2k
                    hash_val = HashUtil::zlib_crc_hash_null(hash_val);
1581
83.2k
                }
1582
15.8M
            }
1583
15.8M
            res_data[i] = hash_val;
1584
15.8M
        }
1585
1586
16.3k
        block.replace_by_position(result, std::move(res_col));
1587
16.3k
        return Status::OK();
1588
16.3k
    }
1589
};
1590
1591
class FunctionUnicodeNormalize : public IFunction {
1592
public:
1593
    static constexpr auto name = "unicode_normalize";
1594
1595
24
    static FunctionPtr create() { return std::make_shared<FunctionUnicodeNormalize>(); }
1596
1597
5
    String get_name() const override { return name; }
1598
1599
15
    size_t get_number_of_arguments() const override { return 2; }
1600
1601
15
    DataTypePtr get_return_type_impl(const DataTypes& arguments) const override {
1602
15
        if (arguments.size() != 2 || !is_string_type(arguments[0]->get_primitive_type()) ||
1603
15
            !is_string_type(arguments[1]->get_primitive_type())) {
1604
0
            throw doris::Exception(ErrorCode::INVALID_ARGUMENT,
1605
0
                                   "Illegal type {} and {} of arguments of function {}",
1606
0
                                   arguments[0]->get_name(), arguments[1]->get_name(), get_name());
1607
0
        }
1608
15
        return arguments[0];
1609
15
    }
1610
1611
16
    ColumnNumbers get_arguments_that_are_always_constant() const override { return {1}; }
1612
1613
32
    Status open(FunctionContext* context, FunctionContext::FunctionStateScope scope) override {
1614
32
        if (scope == FunctionContext::FunctionStateScope::THREAD_LOCAL) {
1615
17
            return Status::OK();
1616
17
        }
1617
1618
15
        if (!context->is_col_constant(1)) {
1619
2
            return Status::InvalidArgument(
1620
2
                    "The second argument 'mode' of function {} must be constant", get_name());
1621
2
        }
1622
1623
13
        auto* const_col = context->get_constant_col(1);
1624
13
        auto mode_ref = const_col->column_ptr->get_data_at(0);
1625
13
        std::string lower_mode = doris::to_lower(std::string(doris::trim(mode_ref.to_string())));
1626
1627
13
        UErrorCode status = U_ZERO_ERROR;
1628
13
        const icu::Normalizer2* normalizer = nullptr;
1629
1630
13
        if (lower_mode == "nfc") {
1631
5
            normalizer = icu::Normalizer2::getInstance(nullptr, "nfc", UNORM2_COMPOSE, status);
1632
8
        } else if (lower_mode == "nfd") {
1633
2
            normalizer = icu::Normalizer2::getNFDInstance(status);
1634
6
        } else if (lower_mode == "nfkc") {
1635
0
            normalizer = icu::Normalizer2::getInstance(nullptr, "nfkc", UNORM2_COMPOSE, status);
1636
6
        } else if (lower_mode == "nfkd") {
1637
2
            normalizer = icu::Normalizer2::getNFKDInstance(status);
1638
4
        } else if (lower_mode == "nfkc_cf") {
1639
2
            normalizer = icu::Normalizer2::getInstance(nullptr, "nfkc_cf", UNORM2_COMPOSE, status);
1640
2
        } else {
1641
2
            return Status::InvalidArgument(
1642
2
                    "Invalid normalization mode '{}' for function {}. "
1643
2
                    "Supported modes: NFC, NFD, NFKC, NFKD, NFKC_CF",
1644
2
                    lower_mode, get_name());
1645
2
        }
1646
1647
11
        if (U_FAILURE(status) || normalizer == nullptr) {
1648
0
            return Status::InvalidArgument(
1649
0
                    "Failed to get normalizer instance for mode '{}' in function {}: {}",
1650
0
                    lower_mode, get_name(), u_errorName(status));
1651
0
        }
1652
1653
11
        auto state = std::make_shared<UnicodeNormalizeState>();
1654
11
        state->normalizer = normalizer;
1655
11
        context->set_function_state(scope, state);
1656
11
        return Status::OK();
1657
11
    }
1658
1659
    Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
1660
11
                        uint32_t result, size_t input_rows_count) const override {
1661
11
        auto* state = reinterpret_cast<UnicodeNormalizeState*>(
1662
11
                context->get_function_state(FunctionContext::FunctionStateScope::FRAGMENT_LOCAL));
1663
11
        if (state == nullptr || state->normalizer == nullptr) {
1664
0
            return Status::RuntimeError("unicode_normalize function state is not initialized");
1665
0
        }
1666
1667
11
        ColumnPtr col =
1668
11
                block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
1669
11
        const auto* col_str = check_and_get_column<ColumnString>(col.get());
1670
11
        if (col_str == nullptr) {
1671
0
            return Status::RuntimeError("Illegal column {} of argument of function {}",
1672
0
                                        block.get_by_position(arguments[0]).column->get_name(),
1673
0
                                        get_name());
1674
0
        }
1675
1676
11
        const auto& data = col_str->get_chars();
1677
11
        const auto& offsets = col_str->get_offsets();
1678
1679
11
        auto res = ColumnString::create();
1680
11
        auto& res_data = res->get_chars();
1681
11
        auto& res_offsets = res->get_offsets();
1682
1683
11
        size_t rows = offsets.size();
1684
11
        res_offsets.resize(rows);
1685
1686
11
        std::string tmp;
1687
22
        for (size_t i = 0; i < rows; ++i) {
1688
11
            const char* begin = reinterpret_cast<const char*>(&data[offsets[i - 1]]);
1689
11
            size_t len = offsets[i] - offsets[i - 1];
1690
1691
11
            normalize_one(state->normalizer, begin, len, tmp);
1692
11
            StringOP::push_value_string(tmp, i, res_data, res_offsets);
1693
11
        }
1694
1695
11
        block.replace_by_position(result, std::move(res));
1696
11
        return Status::OK();
1697
11
    }
1698
1699
private:
1700
    struct UnicodeNormalizeState {
1701
        const icu::Normalizer2* normalizer = nullptr;
1702
    };
1703
1704
    static void normalize_one(const icu::Normalizer2* normalizer, const char* input, size_t length,
1705
11
                              std::string& output) {
1706
11
        if (length == 0) {
1707
1
            output.clear();
1708
1
            return;
1709
1
        }
1710
1711
10
        icu::StringPiece sp(input, static_cast<int32_t>(length));
1712
10
        icu::UnicodeString src16 = icu::UnicodeString::fromUTF8(sp);
1713
1714
10
        UErrorCode status = U_ZERO_ERROR;
1715
10
        UNormalizationCheckResult quick = normalizer->quickCheck(src16, status);
1716
10
        if (U_SUCCESS(status) && quick == UNORM_YES) {
1717
4
            output.assign(input, length);
1718
4
            return;
1719
4
        }
1720
1721
6
        icu::UnicodeString result16;
1722
6
        status = U_ZERO_ERROR;
1723
6
        normalizer->normalize(src16, result16, status);
1724
6
        if (U_FAILURE(status)) {
1725
0
            output.assign(input, length);
1726
0
            return;
1727
0
        }
1728
1729
6
        output.clear();
1730
6
        result16.toUTF8String(output);
1731
6
    }
1732
};
1733
1734
using FunctionMakeSet = FunctionNeedsToHandleNull<MakeSetImpl, PrimitiveType::TYPE_STRING>;
1735
1736
8
void register_function_string_misc(SimpleFunctionFactory& factory) {
1737
8
    factory.register_function<FunctionAutoPartitionName>();
1738
8
    factory.register_function<FunctionConvertTo>();
1739
8
    factory.register_function<FunctionIntToChar>();
1740
8
    factory.register_function<FunctionRandomBytes>();
1741
8
    factory.register_function<FunctionTranslate>();
1742
8
    factory.register_function<FunctionNgramSearch>();
1743
8
    factory.register_function<FunctionXPathString>();
1744
8
    factory.register_function<FunctionCrc32Internal>();
1745
8
    factory.register_function<FunctionMakeSet>();
1746
8
    factory.register_function<FunctionExportSet>();
1747
8
    factory.register_function<FunctionUnicodeNormalize>();
1748
8
}
1749
1750
#include "common/compile_check_avoid_end.h"
1751
} // namespace doris