Coverage Report

Created: 2026-09-30 16:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/be/src/exprs/function/like.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 "exprs/function/like.h"
19
20
#include <fmt/format.h>
21
#include <hs/hs_compile.h>
22
23
#include <cstddef>
24
#include <ostream>
25
#include <utility>
26
#include <vector>
27
28
#include "common/logging.h"
29
#include "core/block/block.h"
30
#include "core/block/column_with_type_and_name.h"
31
#include "core/column/column.h"
32
#include "core/column/column_const.h"
33
#include "core/column/column_vector.h"
34
#include "core/string_ref.h"
35
#include "exprs/function/simple_function_factory.h"
36
#include "util/hyperscan_util.h"
37
38
namespace doris {
39
#include "common/compile_check_begin.h"
40
// A regex to match any regex pattern is equivalent to a substring search.
41
static const RE2 SUBSTRING_RE(R"((?:\.\*)*([^\.\^\{\[\(\|\)\]\}\+\*\?\$\\]*)(?:\.\*)*)");
42
43
// A regex to match any regex pattern which is equivalent to matching a constant string
44
// at the end of the string values.
45
static const RE2 ENDS_WITH_RE(R"((?:\.\*)*([^\.\^\{\[\(\|\)\]\}\+\*\?\$\\]*)\$)");
46
47
// A regex to match any regex pattern which is equivalent to matching a constant string
48
// at the end of the string values.
49
static const RE2 STARTS_WITH_RE(R"(\^([^\.\^\{\[\(\|\)\]\}\+\*\?\$\\]*)(?:\.\*)*)");
50
51
// A regex to match any regex pattern which is equivalent to a constant string match.
52
static const RE2 EQUALS_RE(R"(\^([^\.\^\{\[\(\|\)\]\}\+\*\?\$\\]*)\$)");
53
// A regex to match .*
54
static const RE2 ALLPASS_RE(R"((\.\*)+)");
55
56
// Like patterns
57
static const re2::RE2 LIKE_SUBSTRING_RE(R"((?:%+)(((\\_)|([^%_\\]))+)(?:%+))");
58
static const re2::RE2 LIKE_ENDS_WITH_RE("(?:%+)(((\\\\_)|([^%_]))+)");
59
static const re2::RE2 LIKE_STARTS_WITH_RE(R"((((\\%)|(\\_)|([^%_\\]))+)(?:%+))");
60
static const re2::RE2 LIKE_EQUALS_RE("(((\\\\_)|([^%_]))+)");
61
static const re2::RE2 LIKE_ALLPASS_RE("%+");
62
63
struct VectorAllpassSearchState : public VectorPatternSearchState {
64
402
    VectorAllpassSearchState() : VectorPatternSearchState(FunctionLikeBase::vector_allpass_fn) {}
65
66
402
    ~VectorAllpassSearchState() override = default;
67
68
392
    void like_pattern_match(const std::string& pattern_str) override {
69
392
        if (!pattern_str.empty() && RE2::FullMatch(pattern_str, LIKE_ALLPASS_RE)) {
70
26
            _search_strings->insert_default();
71
366
        } else {
72
366
            _pattern_matched = false;
73
366
        }
74
392
    }
75
76
15
    void regexp_pattern_match(const std::string& pattern_str) override {
77
15
        if (RE2::FullMatch(pattern_str, ALLPASS_RE)) {
78
0
            _search_strings->insert_default();
79
15
        } else {
80
15
            _pattern_matched = false;
81
15
        }
82
15
    }
83
};
84
85
struct VectorEqualSearchState : public VectorPatternSearchState {
86
402
    VectorEqualSearchState() : VectorPatternSearchState(FunctionLikeBase::vector_equals_fn) {}
87
88
402
    ~VectorEqualSearchState() override = default;
89
90
395
    void like_pattern_match(const std::string& pattern_str) override {
91
395
        _search_string.clear();
92
395
        if (pattern_str.empty() || RE2::FullMatch(pattern_str, LIKE_EQUALS_RE, &_search_string)) {
93
92
            FunctionLike::remove_escape_character(&_search_string);
94
92
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
95
303
        } else {
96
303
            _pattern_matched = false;
97
303
        }
98
395
    }
99
100
15
    void regexp_pattern_match(const std::string& pattern_str) override {
101
15
        _search_string.clear();
102
15
        if (RE2::FullMatch(pattern_str, EQUALS_RE, &_search_string)) {
103
0
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
104
15
        } else {
105
15
            _pattern_matched = false;
106
15
        }
107
15
    }
108
};
109
110
struct VectorSubStringSearchState : public VectorPatternSearchState {
111
    VectorSubStringSearchState()
112
402
            : VectorPatternSearchState(FunctionLikeBase::vector_substring_fn) {}
113
114
402
    ~VectorSubStringSearchState() override = default;
115
116
401
    void like_pattern_match(const std::string& pattern_str) override {
117
401
        _search_string.clear();
118
401
        if (RE2::FullMatch(pattern_str, LIKE_SUBSTRING_RE, &_search_string)) {
119
68
            FunctionLike::remove_escape_character(&_search_string);
120
68
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
121
333
        } else {
122
333
            _pattern_matched = false;
123
333
        }
124
401
    }
125
126
15
    void regexp_pattern_match(const std::string& pattern_str) override {
127
15
        _search_string.clear();
128
15
        if (RE2::FullMatch(pattern_str, SUBSTRING_RE, &_search_string)) {
129
0
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
130
15
        } else {
131
15
            _pattern_matched = false;
132
15
        }
133
15
    }
134
};
135
136
struct VectorStartsWithSearchState : public VectorPatternSearchState {
137
    VectorStartsWithSearchState()
138
402
            : VectorPatternSearchState(FunctionLikeBase::vector_starts_with_fn) {}
139
140
402
    ~VectorStartsWithSearchState() override = default;
141
142
393
    void like_pattern_match(const std::string& pattern_str) override {
143
393
        _search_string.clear();
144
393
        if (RE2::FullMatch(pattern_str, LIKE_STARTS_WITH_RE, &_search_string)) {
145
39
            FunctionLike::remove_escape_character(&_search_string);
146
39
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
147
354
        } else {
148
354
            _pattern_matched = false;
149
354
        }
150
393
    }
151
152
15
    void regexp_pattern_match(const std::string& pattern_str) override {
153
15
        _search_string.clear();
154
15
        if (RE2::FullMatch(pattern_str, STARTS_WITH_RE, &_search_string)) {
155
0
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
156
15
        } else {
157
15
            _pattern_matched = false;
158
15
        }
159
15
    }
160
};
161
162
struct VectorEndsWithSearchState : public VectorPatternSearchState {
163
402
    VectorEndsWithSearchState() : VectorPatternSearchState(FunctionLikeBase::vector_ends_with_fn) {}
164
165
402
    ~VectorEndsWithSearchState() override = default;
166
167
392
    void like_pattern_match(const std::string& pattern_str) override {
168
392
        _search_string.clear();
169
392
        if (RE2::FullMatch(pattern_str, LIKE_ENDS_WITH_RE, &_search_string)) {
170
38
            FunctionLike::remove_escape_character(&_search_string);
171
38
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
172
354
        } else {
173
354
            _pattern_matched = false;
174
354
        }
175
392
    }
176
177
15
    void regexp_pattern_match(const std::string& pattern_str) override {
178
15
        _search_string.clear();
179
15
        if (RE2::FullMatch(pattern_str, ENDS_WITH_RE, &_search_string)) {
180
0
            _search_strings->insert_data(_search_string.c_str(), _search_string.size());
181
15
        } else {
182
15
            _pattern_matched = false;
183
15
        }
184
15
    }
185
};
186
187
0
Status LikeSearchState::clone(LikeSearchState& cloned) const {
188
0
    cloned.set_search_string(search_string);
189
0
    cloned.enable_hyperscan_fallback = enable_hyperscan_fallback;
190
0
    cloned.enable_extended_regex = enable_extended_regex;
191
192
0
    std::string re_pattern;
193
0
    FunctionLike::convert_like_pattern(this, pattern_str, &re_pattern);
194
0
    if (hs_database) { // use hyperscan
195
0
        hs_database_t* database = nullptr;
196
0
        hs_scratch_t* scratch = nullptr;
197
0
        RETURN_IF_ERROR(FunctionLike::hs_prepare(nullptr, re_pattern.c_str(), &database, &scratch));
198
199
0
        cloned.hs_database.reset(database);
200
0
        cloned.hs_scratch.reset(scratch);
201
0
    } else { // fallback to re2
202
0
        cloned.hs_database.reset();
203
0
        cloned.hs_scratch.reset();
204
205
0
        RE2::Options opts;
206
0
        opts.set_never_nl(false);
207
0
        opts.set_dot_nl(true);
208
0
        cloned.regex = std::make_unique<RE2>(re_pattern, opts);
209
0
        if (!cloned.regex->ok()) {
210
0
            return Status::InternalError("Invalid regex expression: {}", re_pattern);
211
0
        }
212
0
    }
213
214
0
    return Status::OK();
215
0
}
216
217
Status FunctionLikeBase::constant_allpass_fn(const LikeSearchState* state, const ColumnString& vals,
218
                                             const StringRef& pattern,
219
14
                                             ColumnUInt8::Container& result) {
220
14
    memset(result.data(), 1, vals.size());
221
14
    return Status::OK();
222
14
}
223
224
Status FunctionLikeBase::constant_allpass_fn_scalar(const LikeSearchState* state,
225
                                                    const StringRef& val, const StringRef& pattern,
226
0
                                                    unsigned char* result) {
227
0
    *result = 1;
228
0
    return Status::OK();
229
0
}
230
231
Status FunctionLikeBase::vector_allpass_fn(const ColumnString& vals,
232
                                           const ColumnString& search_strings,
233
21
                                           ColumnUInt8::Container& result) {
234
21
    DCHECK(vals.size() == search_strings.size());
235
21
    DCHECK(vals.size() == result.size());
236
21
    memset(result.data(), 1, vals.size());
237
21
    return Status::OK();
238
21
}
239
240
Status FunctionLikeBase::constant_starts_with_fn(const LikeSearchState* state,
241
                                                 const ColumnString& val, const StringRef& pattern,
242
23
                                                 ColumnUInt8::Container& result) {
243
23
    auto sz = val.size();
244
46
    for (size_t i = 0; i < sz; i++) {
245
23
        const auto& str_ref = val.get_data_at(i);
246
23
        result[i] = (str_ref.size >= state->search_string_sv.size) &&
247
23
                    str_ref.start_with(state->search_string_sv);
248
23
    }
249
23
    return Status::OK();
250
23
}
251
252
Status FunctionLikeBase::constant_starts_with_fn_scalar(const LikeSearchState* state,
253
                                                        const StringRef& val,
254
                                                        const StringRef& pattern,
255
0
                                                        unsigned char* result) {
256
0
    *result = (val.size >= state->search_string_sv.size) &&
257
0
              (state->search_string_sv == val.substring(0, state->search_string_sv.size));
258
0
    return Status::OK();
259
0
}
260
261
Status FunctionLikeBase::vector_starts_with_fn(const ColumnString& vals,
262
                                               const ColumnString& search_strings,
263
33
                                               ColumnUInt8::Container& result) {
264
33
    DCHECK(vals.size() == search_strings.size());
265
33
    DCHECK(vals.size() == result.size());
266
33
    auto sz = vals.size();
267
71
    for (size_t i = 0; i < sz; ++i) {
268
38
        const auto& str_sv = vals.get_data_at(i);
269
38
        const auto& search_string_sv = search_strings.get_data_at(i);
270
38
        result[i] = (str_sv.size >= search_string_sv.size) && str_sv.start_with(search_string_sv);
271
38
    }
272
33
    return Status::OK();
273
33
}
274
275
Status FunctionLikeBase::constant_ends_with_fn(const LikeSearchState* state,
276
                                               const ColumnString& val, const StringRef& pattern,
277
23
                                               ColumnUInt8::Container& result) {
278
23
    auto sz = val.size();
279
46
    for (size_t i = 0; i < sz; i++) {
280
23
        const auto& str_ref = val.get_data_at(i);
281
23
        result[i] = (str_ref.size >= state->search_string_sv.size) &&
282
23
                    str_ref.end_with(state->search_string_sv);
283
23
    }
284
23
    return Status::OK();
285
23
}
286
287
Status FunctionLikeBase::constant_ends_with_fn_scalar(const LikeSearchState* state,
288
                                                      const StringRef& val,
289
                                                      const StringRef& pattern,
290
0
                                                      unsigned char* result) {
291
0
    *result = (val.size >= state->search_string_sv.size) &&
292
0
              (state->search_string_sv == val.substring(val.size - state->search_string_sv.size,
293
0
                                                        state->search_string_sv.size));
294
0
    return Status::OK();
295
0
}
296
297
Status FunctionLikeBase::vector_ends_with_fn(const ColumnString& vals,
298
                                             const ColumnString& search_strings,
299
33
                                             ColumnUInt8::Container& result) {
300
33
    DCHECK(vals.size() == search_strings.size());
301
33
    DCHECK(vals.size() == result.size());
302
33
    auto sz = vals.size();
303
71
    for (size_t i = 0; i < sz; ++i) {
304
38
        const auto& str_sv = vals.get_data_at(i);
305
38
        const auto& search_string_sv = search_strings.get_data_at(i);
306
38
        result[i] = (str_sv.size >= search_string_sv.size) && str_sv.end_with(search_string_sv);
307
38
    }
308
33
    return Status::OK();
309
33
}
310
311
Status FunctionLikeBase::constant_equals_fn(const LikeSearchState* state, const ColumnString& val,
312
                                            const StringRef& pattern,
313
50
                                            ColumnUInt8::Container& result) {
314
50
    auto sz = val.size();
315
100
    for (size_t i = 0; i < sz; i++) {
316
50
        result[i] = (val.get_data_at(i) == state->search_string_sv);
317
50
    }
318
50
    return Status::OK();
319
50
}
320
321
Status FunctionLikeBase::constant_equals_fn_scalar(const LikeSearchState* state,
322
                                                   const StringRef& val, const StringRef& pattern,
323
0
                                                   unsigned char* result) {
324
0
    *result = (val == state->search_string_sv);
325
0
    return Status::OK();
326
0
}
327
328
Status FunctionLikeBase::vector_equals_fn(const ColumnString& vals,
329
                                          const ColumnString& search_strings,
330
84
                                          ColumnUInt8::Container& result) {
331
84
    DCHECK(vals.size() == search_strings.size());
332
84
    DCHECK(vals.size() == result.size());
333
84
    auto sz = vals.size();
334
168
    for (size_t i = 0; i < sz; ++i) {
335
84
        const auto& str_sv = vals.get_data_at(i);
336
84
        const auto& search_string_sv = search_strings.get_data_at(i);
337
84
        result[i] = str_sv == search_string_sv;
338
84
    }
339
84
    return Status::OK();
340
84
}
341
342
Status FunctionLikeBase::constant_substring_fn(const LikeSearchState* state,
343
                                               const ColumnString& val, const StringRef& pattern,
344
45
                                               ColumnUInt8::Container& result) {
345
45
    size_t needle_size = state->search_string_sv.size;
346
45
    if (needle_size == 0) {
347
5
        memset(result.data(), 1, result.size());
348
5
        return Status::OK();
349
5
    }
350
351
40
    const auto& values = val.get_chars();
352
40
    const auto& value_offsets = val.get_offsets();
353
    // treat continuous multi string data as a long string data
354
40
    const UInt8* begin = values.data();
355
40
    const UInt8* end = begin + values.size();
356
40
    const UInt8* pos = begin;
357
358
    /// Current index in the array of strings.
359
40
    size_t i = 0;
360
361
    /// We will search for the next occurrence in all strings at once.
362
71
    while (pos < end) {
363
        // search return matched substring start offset
364
43
        pos = (UInt8*)state->substring_pattern.search((char*)pos, end - pos);
365
43
        if (pos >= end) {
366
12
            break;
367
12
        }
368
369
        /// Determine which index it refers to.
370
        /// begin + value_offsets[i] is the start offset of string at i+1
371
32
        while (i < value_offsets.size() && begin + value_offsets[i] < pos) {
372
1
            ++i;
373
1
        }
374
375
        /// We check that the entry does not pass through the boundaries of strings.
376
31
        if (pos + needle_size <= begin + value_offsets[i]) {
377
28
            result[i] = 1;
378
28
        }
379
380
        // move to next string offset
381
31
        pos = begin + value_offsets[i];
382
31
        ++i;
383
31
    }
384
40
    return Status::OK();
385
45
}
386
387
Status FunctionLikeBase::constant_substring_fn_scalar(const LikeSearchState* state,
388
                                                      const StringRef& val,
389
                                                      const StringRef& pattern,
390
0
                                                      unsigned char* result) {
391
0
    if (state->search_string_sv.size == 0) {
392
0
        *result = true;
393
0
        return Status::OK();
394
0
    }
395
0
    *result = state->substring_pattern.search(val) != -1;
396
0
    return Status::OK();
397
0
}
398
399
Status FunctionLikeBase::vector_substring_fn(const ColumnString& vals,
400
                                             const ColumnString& search_strings,
401
54
                                             ColumnUInt8::Container& result) {
402
54
    DCHECK(vals.size() == search_strings.size());
403
54
    DCHECK(vals.size() == result.size());
404
54
    auto sz = vals.size();
405
108
    for (size_t i = 0; i < sz; ++i) {
406
54
        const auto& str_sv = vals.get_data_at(i);
407
54
        const auto& search_string_sv = search_strings.get_data_at(i);
408
54
        if (search_string_sv.size == 0) {
409
0
            result[i] = true;
410
0
            continue;
411
0
        }
412
54
        doris::StringSearch substring_search(&search_string_sv);
413
54
        result[i] = substring_search.search(str_sv) != -1;
414
54
    }
415
54
    return Status::OK();
416
54
}
417
418
Status FunctionLikeBase::constant_regex_fn_scalar(const LikeSearchState* state,
419
                                                  const StringRef& val, const StringRef& pattern,
420
76
                                                  unsigned char* result) {
421
76
    if (state->hs_database) { // use hyperscan
422
74
        auto ret = hs_scan(state->hs_database.get(), val.data, (int)val.size, 0,
423
74
                           state->hs_scratch.get(), doris::LikeSearchState::hs_match_handler,
424
74
                           (void*)result);
425
74
        if (ret != HS_SUCCESS && ret != HS_SCAN_TERMINATED) {
426
0
            return Status::RuntimeError(fmt::format("hyperscan error: {}", ret));
427
0
        }
428
74
    } else { // re2, or boost::regex for advanced features
429
2
        RETURN_IF_ERROR(regex_search(state->regex.get(), state->boost_regex.get(), val, result));
430
2
    }
431
432
76
    return Status::OK();
433
76
}
434
435
Status FunctionLikeBase::regexp_fn_scalar(const LikeSearchState* state, const StringRef& val,
436
146
                                          const StringRef& pattern, unsigned char* result) {
437
146
    std::unique_ptr<re2::RE2> regex;
438
146
    std::unique_ptr<boost::regex> boost_regex;
439
146
    RETURN_IF_ERROR(compile_regex(std::string_view(pattern.data, pattern.size),
440
146
                                  state->enable_extended_regex, &regex, &boost_regex));
441
141
    RETURN_IF_ERROR(regex_search(regex.get(), boost_regex.get(), val, result));
442
443
140
    return Status::OK();
444
141
}
445
446
Status FunctionLikeBase::constant_regex_fn(const LikeSearchState* state, const ColumnString& val,
447
                                           const StringRef& pattern,
448
105
                                           ColumnUInt8::Container& result) {
449
105
    auto sz = val.size();
450
105
    if (state->hs_database) { // use hyperscan
451
196
        for (size_t i = 0; i < sz; i++) {
452
98
            const auto& str_ref = val.get_data_at(i);
453
98
            auto ret = hs_scan(state->hs_database.get(), str_ref.data, (int)str_ref.size, 0,
454
98
                               state->hs_scratch.get(), doris::LikeSearchState::hs_match_handler,
455
98
                               (void*)(result.data() + i));
456
98
            if (ret != HS_SUCCESS && ret != HS_SCAN_TERMINATED) {
457
0
                return Status::RuntimeError(fmt::format("hyperscan error: {}", ret));
458
0
            }
459
98
        }
460
98
    } else { // re2, or boost::regex for advanced features
461
13
        for (size_t i = 0; i < sz; i++) {
462
7
            RETURN_IF_ERROR(regex_search(state->regex.get(), state->boost_regex.get(),
463
7
                                         val.get_data_at(i), result.data() + i));
464
7
        }
465
7
    }
466
467
104
    return Status::OK();
468
105
}
469
470
Status FunctionLikeBase::regexp_fn(const LikeSearchState* state, const ColumnString& val,
471
15
                                   const StringRef& pattern, ColumnUInt8::Container& result) {
472
15
    std::string re_pattern(pattern.data, pattern.size);
473
474
15
    hs_database_t* database = nullptr;
475
15
    hs_scratch_t* scratch = nullptr;
476
15
    auto hs_status = hs_prepare(nullptr, re_pattern.c_str(), &database, &scratch);
477
15
    if (hs_status.ok()) { // use hyperscan
478
2
        auto sz = val.size();
479
4
        for (size_t i = 0; i < sz; i++) {
480
2
            const auto& str_ref = val.get_data_at(i);
481
2
            auto ret =
482
2
                    hs_scan(database, str_ref.data, (int)str_ref.size, 0, scratch,
483
2
                            doris::LikeSearchState::hs_match_handler, (void*)(result.data() + i));
484
2
            if (ret != HS_SUCCESS && ret != HS_SCAN_TERMINATED) {
485
0
                return Status::RuntimeError(fmt::format("hyperscan error: {}", ret));
486
0
            }
487
2
        }
488
489
2
        hs_free_scratch(scratch);
490
2
        hs_free_database(database);
491
13
    } else { // fallback to re2, then boost::regex for advanced features
492
13
        if (!state->enable_hyperscan_fallback) {
493
3
            return hs_status;
494
3
        }
495
10
        std::unique_ptr<re2::RE2> regex;
496
10
        std::unique_ptr<boost::regex> boost_regex;
497
10
        RETURN_IF_ERROR(
498
10
                compile_regex(re_pattern, state->enable_extended_regex, &regex, &boost_regex));
499
5
        auto sz = val.size();
500
9
        for (size_t i = 0; i < sz; i++) {
501
5
            RETURN_IF_ERROR(regex_search(regex.get(), boost_regex.get(), val.get_data_at(i),
502
5
                                         result.data() + i));
503
5
        }
504
5
    }
505
506
6
    return Status::OK();
507
15
}
508
509
Status FunctionLikeBase::compile_regex(std::string_view pattern, bool enable_extended_regex,
510
                                       std::unique_ptr<re2::RE2>* regex,
511
170
                                       std::unique_ptr<boost::regex>* boost_regex) {
512
170
    RE2::Options opts;
513
170
    opts.set_never_nl(false);
514
170
    opts.set_dot_nl(true);
515
    // A rejected pattern is reported through the returned Status (or handed to Boost.Regex), and
516
    // non-constant patterns are compiled once per row, so keep RE2 from logging every rejection.
517
170
    opts.set_log_errors(false);
518
170
    *regex = std::make_unique<RE2>(re2::StringPiece(pattern.data(), pattern.size()), opts);
519
170
    if ((*regex)->ok()) {
520
140
        return Status::OK();
521
140
    }
522
30
    if (!enable_extended_regex) {
523
12
        return Status::InternalError(
524
12
                "Invalid regex expression: {}. Error: {}. If you need advanced regex features, "
525
12
                "try setting enable_extended_regex=true",
526
12
                pattern, (*regex)->error());
527
12
    }
528
529
    // RE2 failed, fallback to Boost.Regex
530
    // This handles advanced regex features like zero-width assertions
531
18
    regex->reset();
532
18
    try {
533
18
        *boost_regex =
534
18
                std::make_unique<boost::regex>(pattern.data(), pattern.data() + pattern.size());
535
18
    } catch (const boost::regex_error& e) {
536
3
        return Status::InternalError("Invalid regex expression: {}. Error: {}", pattern, e.what());
537
3
    }
538
15
    return Status::OK();
539
18
}
540
541
Status FunctionLikeBase::regex_search(const re2::RE2* regex, const boost::regex* boost_regex,
542
155
                                      const StringRef& val, unsigned char* result) {
543
155
    if (boost_regex != nullptr) {
544
15
        try {
545
15
            *result = boost::regex_search(val.data, val.data + val.size, *boost_regex);
546
15
        } catch (const boost::regex_error& e) {
547
3
            return Status::InternalError("Failed to match regex expression: {}. Error: {}",
548
3
                                         boost_regex->str(), e.what());
549
3
        }
550
12
        return Status::OK();
551
15
    }
552
140
    *result = RE2::PartialMatch(re2::StringPiece(val.data, val.size), *regex);
553
140
    return Status::OK();
554
155
}
555
556
// hyperscan compile expression to database and allocate scratch space
557
221
bool FunctionLikeBase::should_fallback_to_re2(std::string_view regexp) {
558
221
    return is_hyperscan_regexp_expensive(regexp);
559
221
}
560
561
Status FunctionLikeBase::hs_prepare(FunctionContext* context, const char* expression,
562
208
                                    hs_database_t** database, hs_scratch_t** scratch) {
563
208
    if (should_fallback_to_re2(expression)) {
564
10
        *database = nullptr;
565
10
        *scratch = nullptr;
566
        // Callers either fall back to RE2 or return this status based on the session variable.
567
10
        return Status::RuntimeError<false>(HYPERSCAN_BOUNDED_REPEAT_ERROR);
568
10
    }
569
570
198
    hs_compile_error_t* compile_err;
571
198
    auto res = hs_compile(expression, HS_FLAG_DOTALL | HS_FLAG_ALLOWEMPTY | HS_FLAG_UTF8,
572
198
                          HS_MODE_BLOCK, nullptr, database, &compile_err);
573
574
198
    if (res != HS_SUCCESS) {
575
20
        *database = nullptr;
576
20
        std::string error_message = compile_err->message;
577
20
        hs_free_compile_error(compile_err);
578
        // Callers either fall back to RE2 or return this status based on the session variable.
579
20
        return Status::RuntimeError<false>("hs_compile regex pattern error:" + error_message);
580
20
    }
581
178
    hs_free_compile_error(compile_err);
582
583
178
    if (hs_alloc_scratch(*database, scratch) != HS_SUCCESS) {
584
0
        hs_free_database(*database);
585
0
        *database = nullptr;
586
0
        *scratch = nullptr;
587
        // Callers either fall back to RE2 or return this status based on the session variable.
588
0
        return Status::RuntimeError<false>("hs_alloc_scratch allocate scratch space error");
589
0
    }
590
591
178
    return Status::OK();
592
178
}
593
594
Status FunctionLikeBase::execute_impl(FunctionContext* context, Block& block,
595
                                      const ColumnNumbers& arguments, uint32_t result,
596
677
                                      size_t input_rows_count) const {
597
677
    const auto values_col =
598
677
            block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
599
677
    const auto* values = check_and_get_column<ColumnString>(values_col.get());
600
601
677
    if (!values) {
602
0
        return Status::InternalError("Not supported input arguments types");
603
0
    }
604
    // result column
605
677
    auto res = ColumnUInt8::create();
606
677
    ColumnUInt8::Container& vec_res = res->get_data();
607
    // set default value to 0, and match functions only need to set 1/true
608
677
    vec_res.resize_fill(input_rows_count);
609
677
    auto* state = reinterpret_cast<LikeState*>(
610
677
            context->get_function_state(FunctionContext::THREAD_LOCAL));
611
677
    const auto pattern_col = block.get_by_position(arguments[1]).column;
612
677
    if (const auto* str_patterns = check_and_get_column<ColumnString>(pattern_col.get())) {
613
402
        RETURN_IF_ERROR(vector_non_const(*values, *str_patterns, vec_res, state, input_rows_count));
614
402
    } else if (const auto* const_patterns = check_and_get_column<ColumnConst>(pattern_col.get())) {
615
275
        const auto& pattern_val = const_patterns->get_data_at(0);
616
275
        RETURN_IF_ERROR(vector_const(*values, &pattern_val, vec_res, state->function,
617
275
                                     &state->search_state));
618
275
    } else {
619
0
        return Status::InternalError("Not supported input arguments types");
620
0
    }
621
661
    block.replace_by_position(result, std::move(res));
622
661
    return Status::OK();
623
677
}
624
625
Status FunctionLikeBase::vector_const(const ColumnString& values, const StringRef* pattern_val,
626
                                      ColumnUInt8::Container& result, const LikeFn& function,
627
275
                                      LikeSearchState* search_state) const {
628
275
    RETURN_IF_ERROR((function)(search_state, values,
629
275
                               *reinterpret_cast<const StringRef*>(pattern_val), result));
630
265
    return Status::OK();
631
275
}
632
633
template <bool LIKE_PATTERN>
634
402
VPatternSearchStateSPtr FunctionLikeBase::pattern_type_recognition(const ColumnString& patterns) {
635
402
    VPatternSearchStateSPtr allpass_state = std::make_shared<VectorAllpassSearchState>();
636
402
    VPatternSearchStateSPtr equal_state = std::make_shared<VectorEqualSearchState>();
637
402
    VPatternSearchStateSPtr substring_state = std::make_shared<VectorSubStringSearchState>();
638
402
    VPatternSearchStateSPtr starts_with_state = std::make_shared<VectorStartsWithSearchState>();
639
402
    VPatternSearchStateSPtr ends_with_state = std::make_shared<VectorEndsWithSearchState>();
640
402
    size_t size = patterns.size();
641
642
842
    for (size_t i = 0; i < size; ++i) {
643
454
        if (!allpass_state->_pattern_matched && !equal_state->_pattern_matched &&
644
454
            !substring_state->_pattern_matched && !starts_with_state->_pattern_matched &&
645
454
            !ends_with_state->_pattern_matched) {
646
14
            return nullptr;
647
14
        }
648
440
        std::string pattern_str = patterns.get_data_at(i).to_string();
649
440
        if (allpass_state->_pattern_matched) {
650
407
            if constexpr (LIKE_PATTERN) {
651
392
                allpass_state->like_pattern_match(pattern_str);
652
392
            } else {
653
15
                allpass_state->regexp_pattern_match(pattern_str);
654
15
            }
655
407
        }
656
440
        if (equal_state->_pattern_matched) {
657
410
            if constexpr (LIKE_PATTERN) {
658
395
                equal_state->like_pattern_match(pattern_str);
659
395
            } else {
660
15
                equal_state->regexp_pattern_match(pattern_str);
661
15
            }
662
410
        }
663
440
        if (substring_state->_pattern_matched) {
664
416
            if constexpr (LIKE_PATTERN) {
665
401
                substring_state->like_pattern_match(pattern_str);
666
401
            } else {
667
15
                substring_state->regexp_pattern_match(pattern_str);
668
15
            }
669
416
        }
670
440
        if (starts_with_state->_pattern_matched) {
671
408
            if constexpr (LIKE_PATTERN) {
672
393
                starts_with_state->like_pattern_match(pattern_str);
673
393
            } else {
674
15
                starts_with_state->regexp_pattern_match(pattern_str);
675
15
            }
676
408
        }
677
440
        if (ends_with_state->_pattern_matched) {
678
407
            if constexpr (LIKE_PATTERN) {
679
392
                ends_with_state->like_pattern_match(pattern_str);
680
392
            } else {
681
15
                ends_with_state->regexp_pattern_match(pattern_str);
682
15
            }
683
407
        }
684
440
    }
685
686
388
    if (allpass_state->_pattern_matched) {
687
21
        return allpass_state;
688
367
    } else if (equal_state->_pattern_matched) {
689
84
        return equal_state;
690
283
    } else if (substring_state->_pattern_matched) {
691
54
        return substring_state;
692
229
    } else if (starts_with_state->_pattern_matched) {
693
33
        return starts_with_state;
694
196
    } else if (ends_with_state->_pattern_matched) {
695
33
        return ends_with_state;
696
163
    } else {
697
163
        return nullptr;
698
163
    }
699
388
}
_ZN5doris16FunctionLikeBase24pattern_type_recognitionILb1EEESt10shared_ptrINS_24VectorPatternSearchStateEERKNS_9ColumnStrIjEE
Line
Count
Source
634
387
VPatternSearchStateSPtr FunctionLikeBase::pattern_type_recognition(const ColumnString& patterns) {
635
387
    VPatternSearchStateSPtr allpass_state = std::make_shared<VectorAllpassSearchState>();
636
387
    VPatternSearchStateSPtr equal_state = std::make_shared<VectorEqualSearchState>();
637
387
    VPatternSearchStateSPtr substring_state = std::make_shared<VectorSubStringSearchState>();
638
387
    VPatternSearchStateSPtr starts_with_state = std::make_shared<VectorStartsWithSearchState>();
639
387
    VPatternSearchStateSPtr ends_with_state = std::make_shared<VectorEndsWithSearchState>();
640
387
    size_t size = patterns.size();
641
642
812
    for (size_t i = 0; i < size; ++i) {
643
438
        if (!allpass_state->_pattern_matched && !equal_state->_pattern_matched &&
644
438
            !substring_state->_pattern_matched && !starts_with_state->_pattern_matched &&
645
438
            !ends_with_state->_pattern_matched) {
646
13
            return nullptr;
647
13
        }
648
425
        std::string pattern_str = patterns.get_data_at(i).to_string();
649
425
        if (allpass_state->_pattern_matched) {
650
392
            if constexpr (LIKE_PATTERN) {
651
392
                allpass_state->like_pattern_match(pattern_str);
652
            } else {
653
                allpass_state->regexp_pattern_match(pattern_str);
654
            }
655
392
        }
656
425
        if (equal_state->_pattern_matched) {
657
395
            if constexpr (LIKE_PATTERN) {
658
395
                equal_state->like_pattern_match(pattern_str);
659
            } else {
660
                equal_state->regexp_pattern_match(pattern_str);
661
            }
662
395
        }
663
425
        if (substring_state->_pattern_matched) {
664
401
            if constexpr (LIKE_PATTERN) {
665
401
                substring_state->like_pattern_match(pattern_str);
666
            } else {
667
                substring_state->regexp_pattern_match(pattern_str);
668
            }
669
401
        }
670
425
        if (starts_with_state->_pattern_matched) {
671
393
            if constexpr (LIKE_PATTERN) {
672
393
                starts_with_state->like_pattern_match(pattern_str);
673
            } else {
674
                starts_with_state->regexp_pattern_match(pattern_str);
675
            }
676
393
        }
677
425
        if (ends_with_state->_pattern_matched) {
678
392
            if constexpr (LIKE_PATTERN) {
679
392
                ends_with_state->like_pattern_match(pattern_str);
680
            } else {
681
                ends_with_state->regexp_pattern_match(pattern_str);
682
            }
683
392
        }
684
425
    }
685
686
374
    if (allpass_state->_pattern_matched) {
687
21
        return allpass_state;
688
353
    } else if (equal_state->_pattern_matched) {
689
84
        return equal_state;
690
269
    } else if (substring_state->_pattern_matched) {
691
54
        return substring_state;
692
215
    } else if (starts_with_state->_pattern_matched) {
693
33
        return starts_with_state;
694
182
    } else if (ends_with_state->_pattern_matched) {
695
33
        return ends_with_state;
696
149
    } else {
697
149
        return nullptr;
698
149
    }
699
374
}
_ZN5doris16FunctionLikeBase24pattern_type_recognitionILb0EEESt10shared_ptrINS_24VectorPatternSearchStateEERKNS_9ColumnStrIjEE
Line
Count
Source
634
15
VPatternSearchStateSPtr FunctionLikeBase::pattern_type_recognition(const ColumnString& patterns) {
635
15
    VPatternSearchStateSPtr allpass_state = std::make_shared<VectorAllpassSearchState>();
636
15
    VPatternSearchStateSPtr equal_state = std::make_shared<VectorEqualSearchState>();
637
15
    VPatternSearchStateSPtr substring_state = std::make_shared<VectorSubStringSearchState>();
638
15
    VPatternSearchStateSPtr starts_with_state = std::make_shared<VectorStartsWithSearchState>();
639
15
    VPatternSearchStateSPtr ends_with_state = std::make_shared<VectorEndsWithSearchState>();
640
15
    size_t size = patterns.size();
641
642
30
    for (size_t i = 0; i < size; ++i) {
643
16
        if (!allpass_state->_pattern_matched && !equal_state->_pattern_matched &&
644
16
            !substring_state->_pattern_matched && !starts_with_state->_pattern_matched &&
645
16
            !ends_with_state->_pattern_matched) {
646
1
            return nullptr;
647
1
        }
648
15
        std::string pattern_str = patterns.get_data_at(i).to_string();
649
15
        if (allpass_state->_pattern_matched) {
650
            if constexpr (LIKE_PATTERN) {
651
                allpass_state->like_pattern_match(pattern_str);
652
15
            } else {
653
15
                allpass_state->regexp_pattern_match(pattern_str);
654
15
            }
655
15
        }
656
15
        if (equal_state->_pattern_matched) {
657
            if constexpr (LIKE_PATTERN) {
658
                equal_state->like_pattern_match(pattern_str);
659
15
            } else {
660
15
                equal_state->regexp_pattern_match(pattern_str);
661
15
            }
662
15
        }
663
15
        if (substring_state->_pattern_matched) {
664
            if constexpr (LIKE_PATTERN) {
665
                substring_state->like_pattern_match(pattern_str);
666
15
            } else {
667
15
                substring_state->regexp_pattern_match(pattern_str);
668
15
            }
669
15
        }
670
15
        if (starts_with_state->_pattern_matched) {
671
            if constexpr (LIKE_PATTERN) {
672
                starts_with_state->like_pattern_match(pattern_str);
673
15
            } else {
674
15
                starts_with_state->regexp_pattern_match(pattern_str);
675
15
            }
676
15
        }
677
15
        if (ends_with_state->_pattern_matched) {
678
            if constexpr (LIKE_PATTERN) {
679
                ends_with_state->like_pattern_match(pattern_str);
680
15
            } else {
681
15
                ends_with_state->regexp_pattern_match(pattern_str);
682
15
            }
683
15
        }
684
15
    }
685
686
14
    if (allpass_state->_pattern_matched) {
687
0
        return allpass_state;
688
14
    } else if (equal_state->_pattern_matched) {
689
0
        return equal_state;
690
14
    } else if (substring_state->_pattern_matched) {
691
0
        return substring_state;
692
14
    } else if (starts_with_state->_pattern_matched) {
693
0
        return starts_with_state;
694
14
    } else if (ends_with_state->_pattern_matched) {
695
0
        return ends_with_state;
696
14
    } else {
697
14
        return nullptr;
698
14
    }
699
14
}
700
701
Status FunctionLikeBase::vector_non_const(const ColumnString& values, const ColumnString& patterns,
702
                                          ColumnUInt8::Container& result, LikeState* state,
703
402
                                          size_t input_rows_count) const {
704
402
    ColumnString::MutablePtr replaced_patterns;
705
402
    VPatternSearchStateSPtr vector_search_state;
706
402
    if (state->is_like_pattern) {
707
387
        if (state->has_custom_escape) {
708
0
            replaced_patterns = ColumnString::create();
709
0
            for (int i = 0; i < input_rows_count; ++i) {
710
0
                std::string val =
711
0
                        replace_pattern_by_escape(patterns.get_data_at(i), state->escape_char);
712
0
                replaced_patterns->insert_data(val.c_str(), val.size());
713
0
            }
714
0
            vector_search_state = pattern_type_recognition<true>(*replaced_patterns);
715
387
        } else {
716
387
            vector_search_state = pattern_type_recognition<true>(patterns);
717
387
        }
718
387
    } else {
719
15
        vector_search_state = pattern_type_recognition<false>(patterns);
720
15
    }
721
722
402
    const ColumnString& real_pattern = state->has_custom_escape ? *replaced_patterns : patterns;
723
724
402
    if (vector_search_state == nullptr) {
725
        // pattern type recognition failed, use default case
726
517
        for (int i = 0; i < input_rows_count; ++i) {
727
346
            const auto pattern_val = real_pattern.get_data_at(i);
728
346
            const auto value_val = values.get_data_at(i);
729
346
            RETURN_IF_ERROR((state->scalar_function)(&state->search_state, value_val, pattern_val,
730
346
                                                     &result[i]));
731
346
        }
732
171
        return Status::OK();
733
177
    }
734
225
    const auto* search_strings =
735
225
            static_cast<const ColumnString*>(vector_search_state->_search_strings.get());
736
225
    return (vector_search_state->_vector_function)(values, *search_strings, result);
737
402
}
738
739
Status FunctionLike::like_fn(const LikeSearchState* state, const ColumnString& val,
740
1
                             const StringRef& pattern, ColumnUInt8::Container& result) {
741
1
    std::string re_pattern;
742
1
    convert_like_pattern(state, std::string(pattern.data, pattern.size), &re_pattern);
743
1
    return regexp_fn(state, val, {re_pattern.c_str(), re_pattern.size()}, result);
744
1
}
745
746
Status FunctionLike::like_fn_scalar(const LikeSearchState* state, const StringRef& val,
747
256
                                    const StringRef& pattern, unsigned char* result) {
748
    // Try to use fast path to avoid regex compilation
749
256
    std::string search_string;
750
256
    LikeFastPath fast_path = extract_like_fast_path(pattern.data, pattern.size, search_string);
751
752
256
    switch (fast_path) {
753
6
    case LikeFastPath::ALLPASS:
754
6
        *result = 1;
755
6
        return Status::OK();
756
62
    case LikeFastPath::EQUALS:
757
62
        *result = (val.size == search_string.size() &&
758
62
                   (search_string.empty() ||
759
34
                    memcmp(val.data, search_string.data(), search_string.size()) == 0));
760
62
        return Status::OK();
761
17
    case LikeFastPath::STARTS_WITH:
762
17
        *result = (val.size >= search_string.size() &&
763
17
                   memcmp(val.data, search_string.data(), search_string.size()) == 0);
764
17
        return Status::OK();
765
11
    case LikeFastPath::ENDS_WITH:
766
11
        *result = (val.size >= search_string.size() &&
767
11
                   memcmp(val.data + val.size - search_string.size(), search_string.data(),
768
10
                          search_string.size()) == 0);
769
11
        return Status::OK();
770
28
    case LikeFastPath::SUBSTRING:
771
28
        if (search_string.empty()) {
772
0
            *result = 1;
773
28
        } else {
774
            // Use memmem for substring search
775
28
            *result = (memmem(val.data, val.size, search_string.data(), search_string.size()) !=
776
28
                       nullptr);
777
28
        }
778
28
        return Status::OK();
779
132
    case LikeFastPath::REGEX:
780
132
    default:
781
        // Fall back to regex matching
782
132
        std::string re_pattern;
783
132
        convert_like_pattern(state, std::string(pattern.data, pattern.size), &re_pattern);
784
132
        return regexp_fn_scalar(state, StringRef(val.data, val.size),
785
132
                                {re_pattern.c_str(), re_pattern.size()}, result);
786
256
    }
787
256
}
788
789
void FunctionLike::convert_like_pattern(const LikeSearchState* state, const std::string& pattern,
790
295
                                        std::string* re_pattern) {
791
295
    re_pattern->clear();
792
793
295
    if (pattern.empty()) {
794
        // `\z` is the end of the value in both Hyperscan and RE2
795
1
        re_pattern->append("^\\z");
796
1
        return;
797
1
    }
798
799
    // add ^ to pattern head to match line head
800
294
    if (!pattern.empty() && pattern[0] != '%') {
801
256
        re_pattern->append("^");
802
256
    }
803
804
    // expect % and _, all chars should keep it literal mean.
805
1.74k
    for (size_t i = 0; i < pattern.size(); i++) {
806
1.51k
        char c = pattern[i];
807
1.51k
        if (c == '\\' && i + 1 < pattern.size()) {
808
47
            char next_c = pattern[i + 1];
809
47
            if (next_c == '%' || next_c == '_') {
810
                // convert "\%" and "\_" to literal "%" and "_"
811
25
                re_pattern->append(1, next_c);
812
25
                i++;
813
25
                continue;
814
25
            } else if (next_c == '\\') {
815
                // keep valid escape "\\"
816
6
                re_pattern->append("\\\\");
817
6
                i++;
818
6
                continue;
819
6
            }
820
47
        }
821
822
1.48k
        if (c == '%') {
823
226
            if (i + 1 == pattern.size()) {
824
                // a trailing `%` matches anything, and so does appending nothing
825
71
                return;
826
71
            }
827
155
            re_pattern->append(".*");
828
1.26k
        } else if (c == '_') {
829
335
            re_pattern->append(".");
830
925
        } else {
831
            // special for hyperscan: [, ], (, ), {, }, -, *, +, \, |, /, :, ^, ., $, ?
832
925
            if (c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' || c == '-' ||
833
925
                c == '*' || c == '+' || c == '\\' || c == '|' || c == '/' || c == ':' || c == '^' ||
834
925
                c == '.' || c == '$' || c == '?') {
835
46
                re_pattern->append(1, '\\');
836
46
            }
837
925
            re_pattern->append(1, c);
838
925
        }
839
1.48k
    }
840
841
    // `\z` is the end of the value in both Hyperscan and RE2
842
223
    re_pattern->append("\\z");
843
223
}
844
845
462
void FunctionLike::remove_escape_character(std::string* search_string) {
846
462
    std::string tmp_search_string;
847
462
    tmp_search_string.swap(*search_string);
848
462
    int64_t len = tmp_search_string.length();
849
    // sometime 'like' may allowed converted to 'equals/start_with/end_with/sub_with'
850
    // so we need to remove escape from pattern to construct search string and use to do 'equals/start_with/end_with/sub_with'
851
2.04k
    for (int i = 0; i < len;) {
852
1.57k
        if (tmp_search_string[i] == '\\' && i + 1 < len &&
853
1.57k
            (tmp_search_string[i + 1] == '%' || tmp_search_string[i + 1] == '_' ||
854
44
             tmp_search_string[i + 1] == '\\')) {
855
36
            search_string->append(1, tmp_search_string[i + 1]);
856
36
            i += 2;
857
1.54k
        } else {
858
1.54k
            search_string->append(1, tmp_search_string[i]);
859
1.54k
            i++;
860
1.54k
        }
861
1.57k
    }
862
462
}
863
864
0
bool re2_full_match(const std::string& str, const RE2& re, std::vector<std::string>& results) {
865
0
    if (!re.ok()) {
866
0
        return false;
867
0
    }
868
869
0
    std::vector<RE2::Arg> arguments;
870
0
    std::vector<RE2::Arg*> arguments_ptrs;
871
0
    std::size_t args_count = re.NumberOfCapturingGroups();
872
0
    arguments.resize(args_count);
873
0
    arguments_ptrs.resize(args_count);
874
0
    results.resize(args_count);
875
0
    for (std::size_t i = 0; i < args_count; ++i) {
876
0
        arguments[i] = &results[i];
877
0
        arguments_ptrs[i] = &arguments[i];
878
0
    }
879
880
0
    return RE2::FullMatchN(str, re, arguments_ptrs.data(), (int)args_count);
881
0
}
882
883
0
void verbose_log_match(const std::string& str, const std::string& pattern_name, const RE2& re) {
884
0
    std::vector<std::string> results;
885
0
    VLOG_DEBUG << "arg str: " << str << ", size: " << str.size() << ", pattern " << pattern_name
886
0
               << ": " << re.pattern() << ", size: " << re.pattern().size();
887
0
    if (re2_full_match(str, re, results)) {
888
0
        for (int i = 0; i < results.size(); ++i) {
889
0
            VLOG_DEBUG << "match " << i << ": " << results[i] << ", size: " << results[i].size();
890
0
        }
891
0
    } else {
892
0
        VLOG_DEBUG << "no match";
893
0
    }
894
0
}
895
896
Status FunctionLike::construct_like_const_state(FunctionContext* context, const StringRef& pattern,
897
                                                std::shared_ptr<LikeState>& state,
898
400
                                                bool try_hyperscan) {
899
400
    std::string pattern_str;
900
400
    if (state->has_custom_escape) {
901
1
        pattern_str = replace_pattern_by_escape(pattern, state->escape_char);
902
399
    } else {
903
399
        pattern_str = pattern.to_string();
904
399
    }
905
400
    state->search_state.pattern_str = pattern_str;
906
400
    std::string search_string;
907
908
400
    if (!pattern_str.empty() && RE2::FullMatch(pattern_str, LIKE_ALLPASS_RE)) {
909
22
        state->search_state.set_search_string("");
910
22
        state->function = constant_allpass_fn;
911
22
        state->scalar_function = constant_allpass_fn_scalar;
912
378
    } else if (pattern_str.empty() || RE2::FullMatch(pattern_str, LIKE_EQUALS_RE, &search_string)) {
913
101
        if (VLOG_DEBUG_IS_ON) {
914
0
            verbose_log_match(pattern_str, "LIKE_EQUALS_RE", LIKE_EQUALS_RE);
915
0
            VLOG_DEBUG << "search_string : " << search_string << ", size: " << search_string.size();
916
0
        }
917
101
        remove_escape_character(&search_string);
918
101
        if (VLOG_DEBUG_IS_ON) {
919
0
            VLOG_DEBUG << "search_string escape removed: " << search_string
920
0
                       << ", size: " << search_string.size();
921
0
        }
922
101
        state->search_state.set_search_string(search_string);
923
101
        state->function = constant_equals_fn;
924
101
        state->scalar_function = constant_equals_fn_scalar;
925
277
    } else if (RE2::FullMatch(pattern_str, LIKE_STARTS_WITH_RE, &search_string)) {
926
34
        if (VLOG_DEBUG_IS_ON) {
927
0
            verbose_log_match(pattern_str, "LIKE_STARTS_WITH_RE", LIKE_STARTS_WITH_RE);
928
0
            VLOG_DEBUG << "search_string : " << search_string << ", size: " << search_string.size();
929
0
        }
930
34
        remove_escape_character(&search_string);
931
34
        if (VLOG_DEBUG_IS_ON) {
932
0
            VLOG_DEBUG << "search_string escape removed: " << search_string
933
0
                       << ", size: " << search_string.size();
934
0
        }
935
34
        state->search_state.set_search_string(search_string);
936
34
        state->function = constant_starts_with_fn;
937
34
        state->scalar_function = constant_starts_with_fn_scalar;
938
243
    } else if (RE2::FullMatch(pattern_str, LIKE_ENDS_WITH_RE, &search_string)) {
939
34
        if (VLOG_DEBUG_IS_ON) {
940
0
            verbose_log_match(pattern_str, "LIKE_ENDS_WITH_RE", LIKE_ENDS_WITH_RE);
941
0
            VLOG_DEBUG << "search_string : " << search_string << ", size: " << search_string.size();
942
0
        }
943
34
        remove_escape_character(&search_string);
944
34
        if (VLOG_DEBUG_IS_ON) {
945
0
            VLOG_DEBUG << "search_string escape removed: " << search_string
946
0
                       << ", size: " << search_string.size();
947
0
        }
948
34
        state->search_state.set_search_string(search_string);
949
34
        state->function = constant_ends_with_fn;
950
34
        state->scalar_function = constant_ends_with_fn_scalar;
951
209
    } else if (RE2::FullMatch(pattern_str, LIKE_SUBSTRING_RE, &search_string)) {
952
56
        if (VLOG_DEBUG_IS_ON) {
953
0
            verbose_log_match(pattern_str, "LIKE_SUBSTRING_RE", LIKE_SUBSTRING_RE);
954
0
            VLOG_DEBUG << "search_string : " << search_string << ", size: " << search_string.size();
955
0
        }
956
56
        remove_escape_character(&search_string);
957
56
        if (VLOG_DEBUG_IS_ON) {
958
0
            VLOG_DEBUG << "search_string escape removed: " << search_string
959
0
                       << ", size: " << search_string.size();
960
0
        }
961
56
        state->search_state.set_search_string(search_string);
962
56
        state->function = constant_substring_fn;
963
56
        state->scalar_function = constant_substring_fn_scalar;
964
153
    } else {
965
153
        std::string re_pattern;
966
153
        convert_like_pattern(&state->search_state, pattern_str, &re_pattern);
967
153
        if (VLOG_DEBUG_IS_ON) {
968
0
            VLOG_DEBUG << "hyperscan, pattern str: " << pattern_str
969
0
                       << ", size: " << pattern_str.size() << ", re pattern: " << re_pattern
970
0
                       << ", size: " << re_pattern.size();
971
0
        }
972
973
153
        hs_database_t* database = nullptr;
974
153
        hs_scratch_t* scratch = nullptr;
975
153
        Status hs_status;
976
153
        if (try_hyperscan) {
977
153
            hs_status = hs_prepare(context, re_pattern.c_str(), &database, &scratch);
978
153
        }
979
153
        if (try_hyperscan && hs_status.ok()) {
980
            // use hyperscan
981
153
            state->search_state.hs_database.reset(database);
982
153
            state->search_state.hs_scratch.reset(scratch);
983
153
        } else {
984
            // fallback to re2
985
0
            if (try_hyperscan && !state->search_state.enable_hyperscan_fallback) {
986
0
                return hs_status;
987
0
            }
988
            // reset hs_database to nullptr to indicate not use hyperscan
989
0
            state->search_state.hs_database.reset();
990
0
            state->search_state.hs_scratch.reset();
991
992
0
            RE2::Options opts;
993
0
            opts.set_never_nl(false);
994
0
            opts.set_dot_nl(true);
995
0
            state->search_state.regex = std::make_unique<RE2>(re_pattern, opts);
996
0
            if (!state->search_state.regex->ok()) {
997
0
                return Status::InternalError("Invalid regex expression: {}(origin: {})", re_pattern,
998
0
                                             pattern_str);
999
0
            }
1000
0
        }
1001
1002
153
        state->function = constant_regex_fn;
1003
153
        state->scalar_function = constant_regex_fn_scalar;
1004
153
    }
1005
400
    return Status::OK();
1006
400
}
1007
1008
1.23k
Status FunctionLike::open(FunctionContext* context, FunctionContext::FunctionStateScope scope) {
1009
1.23k
    if (scope != FunctionContext::THREAD_LOCAL) {
1010
615
        return Status::OK();
1011
615
    }
1012
617
    std::shared_ptr<LikeState> state = std::make_shared<LikeState>();
1013
617
    state->is_like_pattern = true;
1014
617
    state->search_state.enable_hyperscan_fallback =
1015
617
            context->state()->query_options().enable_hyperscan_fallback;
1016
617
    state->function = like_fn;
1017
617
    state->scalar_function = like_fn_scalar;
1018
617
    if (context->is_col_constant(2)) {
1019
1
        state->has_custom_escape = true;
1020
1
        const auto escape_col = context->get_constant_col(2)->column_ptr;
1021
1
        const auto& escape = escape_col->get_data_at(0);
1022
1
        if (escape.size != 1) {
1023
0
            return Status::InternalError("Escape character must be a single character, got: {}",
1024
0
                                         escape.to_string());
1025
0
        }
1026
1
        state->escape_char = escape.data[0];
1027
1
    }
1028
617
    if (context->is_col_constant(1)) {
1029
400
        const auto pattern_col = context->get_constant_col(1)->column_ptr;
1030
400
        const auto& pattern = pattern_col->get_data_at(0);
1031
400
        RETURN_IF_ERROR(construct_like_const_state(context, pattern, state));
1032
400
    }
1033
617
    context->set_function_state(scope, state);
1034
1035
617
    return Status::OK();
1036
617
}
1037
1038
Status FunctionRegexpLike::open(FunctionContext* context,
1039
184
                                FunctionContext::FunctionStateScope scope) {
1040
184
    if (scope != FunctionContext::THREAD_LOCAL) {
1041
72
        return Status::OK();
1042
72
    }
1043
112
    std::shared_ptr<LikeState> state = std::make_shared<LikeState>();
1044
112
    context->set_function_state(scope, state);
1045
112
    state->is_like_pattern = false;
1046
112
    state->search_state.enable_hyperscan_fallback =
1047
112
            context->state()->query_options().enable_hyperscan_fallback;
1048
112
    state->search_state.enable_extended_regex = context->state()->enable_extended_regex();
1049
112
    state->function = regexp_fn;
1050
112
    state->scalar_function = regexp_fn_scalar;
1051
112
    if (context->is_col_constant(1)) {
1052
85
        const auto pattern_col = context->get_constant_col(1)->column_ptr;
1053
85
        const auto& pattern = pattern_col->get_data_at(0);
1054
1055
85
        std::string pattern_str = pattern.to_string();
1056
85
        std::string search_string;
1057
85
        if (RE2::FullMatch(pattern_str, ALLPASS_RE)) {
1058
4
            state->search_state.set_search_string("");
1059
4
            state->function = constant_allpass_fn;
1060
4
            state->scalar_function = constant_allpass_fn_scalar;
1061
81
        } else if (RE2::FullMatch(pattern_str, EQUALS_RE, &search_string)) {
1062
7
            state->search_state.set_search_string(search_string);
1063
7
            state->function = constant_equals_fn;
1064
7
            state->scalar_function = constant_equals_fn_scalar;
1065
74
        } else if (RE2::FullMatch(pattern_str, STARTS_WITH_RE, &search_string)) {
1066
7
            state->search_state.set_search_string(search_string);
1067
7
            state->function = constant_starts_with_fn;
1068
7
            state->scalar_function = constant_starts_with_fn_scalar;
1069
67
        } else if (RE2::FullMatch(pattern_str, ENDS_WITH_RE, &search_string)) {
1070
7
            state->search_state.set_search_string(search_string);
1071
7
            state->function = constant_ends_with_fn;
1072
7
            state->scalar_function = constant_ends_with_fn_scalar;
1073
60
        } else if (RE2::FullMatch(pattern_str, SUBSTRING_RE, &search_string)) {
1074
20
            state->search_state.set_search_string(search_string);
1075
20
            state->function = constant_substring_fn;
1076
20
            state->scalar_function = constant_substring_fn_scalar;
1077
40
        } else {
1078
40
            hs_database_t* database = nullptr;
1079
40
            hs_scratch_t* scratch = nullptr;
1080
40
            auto hs_status = hs_prepare(context, pattern_str.c_str(), &database, &scratch);
1081
40
            if (hs_status.ok()) {
1082
                // use hyperscan
1083
23
                state->search_state.hs_database.reset(database);
1084
23
                state->search_state.hs_scratch.reset(scratch);
1085
23
            } else {
1086
                // fallback to re2
1087
17
                if (!state->search_state.enable_hyperscan_fallback) {
1088
3
                    return hs_status;
1089
3
                }
1090
                // reset hs_database to nullptr to indicate not use hyperscan
1091
14
                state->search_state.hs_database.reset();
1092
14
                state->search_state.hs_scratch.reset();
1093
14
                RETURN_IF_ERROR(compile_regex(
1094
14
                        pattern_str, state->search_state.enable_extended_regex,
1095
14
                        &state->search_state.regex, &state->search_state.boost_regex));
1096
14
            }
1097
32
            state->function = constant_regex_fn;
1098
32
            state->scalar_function = constant_regex_fn_scalar;
1099
32
        }
1100
85
    }
1101
104
    return Status::OK();
1102
112
}
1103
1104
1
void register_function_like(SimpleFunctionFactory& factory) {
1105
1
    factory.register_function<FunctionLike>();
1106
1
}
1107
1108
1
void register_function_regexp(SimpleFunctionFactory& factory) {
1109
1
    factory.register_function<FunctionRegexpLike>();
1110
1
    factory.register_alias(FunctionRegexpLike::name, FunctionRegexpLike::alias);
1111
1
}
1112
#include "common/compile_check_end.h"
1113
} // namespace doris