Coverage Report

Created: 2026-10-15 19:32

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