Coverage Report

Created: 2026-10-17 16:40

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