This is an automated email from the ASF dual-hosted git repository.
csun5285 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new fd4f8acc03e [fix](regexp) Apply extended regex fallback to
non-constant patterns (#68501)
fd4f8acc03e is described below
commit fd4f8acc03ececbd9896043a3dbad9c976f1245f
Author: Jerry Hu <[email protected]>
AuthorDate: Mon Sep 28 11:17:23 2026 +0800
[fix](regexp) Apply extended regex fallback to non-constant patterns
(#68501)
With `enable_extended_regex = true`, a REGEXP/RLIKE pattern that RE2
rejects
(for example lookaround assertions such as `(?<=foo)bar` or
`foo(?=123)`) is
compiled with Boost.Regex instead. That fallback only existed in the
constant
pattern path built during `open()`. When the pattern comes from a
column, or
from a constant that is only visible at execute time, `regexp_fn` and
`regexp_fn_scalar` compiled the pattern with RE2 only and failed the
query
with `Invalid pattern: foo(?=123)` even though the same pattern worked
as a
constant.
Reproduce:
```sql
SET enable_extended_regex = true;
CREATE TABLE regex_patterns (s VARCHAR(32), p VARCHAR(64))
DISTRIBUTED BY HASH(s) BUCKETS 1 PROPERTIES ("replication_num" = "1");
INSERT INTO regex_patterns VALUES ('foobar', '(?<=foo)bar'), ('foo123bar',
'foo(?=123)');
SELECT REGEXP('foobar', '(?<=foo)bar'); -- 1
SELECT s, p, s REGEXP p FROM regex_patterns; -- ERROR: Invalid
pattern: foo(?=123)
```
Fix: move the RE2-then-Boost compilation into a shared
`FunctionLikeBase::compile_regex` helper and a matching `regex_search`
helper, store `enable_extended_regex` in `LikeSearchState` next to
`enable_hyperscan_fallback`, and use the helpers in the constant path,
the
per-batch `regexp_fn` path and the per-row `regexp_fn_scalar` path.
After the
fix the column query returns `1` for both rows; when the session
variable is
off, all three paths report the same
`Invalid regex expression: ... try setting enable_extended_regex=true`
error
instead of the bare `Invalid pattern` message.
Two related hardenings in the same helpers: RE2 no longer logs every
rejected
pattern to stderr (the error text is returned in the Status, and column
patterns are compiled once per row), and a `boost::regex_error` raised
while
matching (Boost gives up on pathological patterns such as `(?=a)(a+)+b`
once
its backtracking budget is exhausted) is converted into a non-OK Status
instead of escaping as a C++ exception, which would otherwise terminate
the
process when the predicate is evaluated on a scanner thread.
---
be/src/exprs/function/like.cpp | 131 +++++++++++----------
be/src/exprs/function/like.h | 17 +++
be/test/exprs/function/function_like_test.cpp | 104 ++++++++++++++--
.../test_regexp_extended_nonconstant_pattern.out | 28 +++++
...test_regexp_extended_nonconstant_pattern.groovy | 85 +++++++++++++
5 files changed, 298 insertions(+), 67 deletions(-)
diff --git a/be/src/exprs/function/like.cpp b/be/src/exprs/function/like.cpp
index 2dfd08d3b51..c02f9875cc0 100644
--- a/be/src/exprs/function/like.cpp
+++ b/be/src/exprs/function/like.cpp
@@ -187,6 +187,7 @@ struct VectorEndsWithSearchState : public
VectorPatternSearchState {
Status LikeSearchState::clone(LikeSearchState& cloned) const {
cloned.set_search_string(search_string);
cloned.enable_hyperscan_fallback = enable_hyperscan_fallback;
+ cloned.enable_extended_regex = enable_extended_regex;
std::string re_pattern;
FunctionLike::convert_like_pattern(this, pattern_str, &re_pattern);
@@ -424,10 +425,8 @@ Status FunctionLikeBase::constant_regex_fn_scalar(const
LikeSearchState* state,
if (ret != HS_SUCCESS && ret != HS_SCAN_TERMINATED) {
return Status::RuntimeError(fmt::format("hyperscan error: {}",
ret));
}
- } else if (state->boost_regex) { // use boost::regex for advanced features
- *result = boost::regex_search(val.data, val.data + val.size,
*state->boost_regex);
- } else { // fallback to re2
- *result = RE2::PartialMatch(re2::StringPiece(val.data, val.size),
*state->regex);
+ } else { // re2, or boost::regex for advanced features
+ RETURN_IF_ERROR(regex_search(state->regex.get(),
state->boost_regex.get(), val, result));
}
return Status::OK();
@@ -435,15 +434,11 @@ Status FunctionLikeBase::constant_regex_fn_scalar(const
LikeSearchState* state,
Status FunctionLikeBase::regexp_fn_scalar(const LikeSearchState* state, const
StringRef& val,
const StringRef& pattern, unsigned
char* result) {
- RE2::Options opts;
- opts.set_never_nl(false);
- opts.set_dot_nl(true);
- re2::RE2 re(re2::StringPiece(pattern.data, pattern.size), opts);
- if (re.ok()) {
- *result = RE2::PartialMatch(re2::StringPiece(val.data, val.size), re);
- } else {
- return Status::RuntimeError("Invalid pattern: {}",
pattern.debug_string());
- }
+ std::unique_ptr<re2::RE2> regex;
+ std::unique_ptr<boost::regex> boost_regex;
+ RETURN_IF_ERROR(compile_regex(std::string_view(pattern.data, pattern.size),
+ state->enable_extended_regex, ®ex,
&boost_regex));
+ RETURN_IF_ERROR(regex_search(regex.get(), boost_regex.get(), val, result));
return Status::OK();
}
@@ -462,17 +457,10 @@ Status FunctionLikeBase::constant_regex_fn(const
LikeSearchState* state, const C
return Status::RuntimeError(fmt::format("hyperscan error: {}",
ret));
}
}
- } else if (state->boost_regex) { // use boost::regex for advanced features
+ } else { // re2, or boost::regex for advanced features
for (size_t i = 0; i < sz; i++) {
- const auto& str_ref = val.get_data_at(i);
- *(result.data() + i) = boost::regex_search(str_ref.data,
str_ref.data + str_ref.size,
- *state->boost_regex);
- }
- } else { // fallback to re2
- for (size_t i = 0; i < sz; i++) {
- const auto& str_ref = val.get_data_at(i);
- *(result.data() + i) =
- RE2::PartialMatch(re2::StringPiece(str_ref.data,
str_ref.size), *state->regex);
+ RETURN_IF_ERROR(regex_search(state->regex.get(),
state->boost_regex.get(),
+ val.get_data_at(i), result.data() +
i));
}
}
@@ -500,29 +488,71 @@ Status FunctionLikeBase::regexp_fn(const LikeSearchState*
state, const ColumnStr
hs_free_scratch(scratch);
hs_free_database(database);
- } else { // fallback to re2
+ } else { // fallback to re2, then boost::regex for advanced features
if (!state->enable_hyperscan_fallback) {
return hs_status;
}
- RE2::Options opts;
- opts.set_never_nl(false);
- opts.set_dot_nl(true);
- re2::RE2 re(re_pattern, opts);
- if (re.ok()) {
- auto sz = val.size();
- for (size_t i = 0; i < sz; i++) {
- const auto& str_ref = val.get_data_at(i);
- *(result.data() + i) =
- RE2::PartialMatch(re2::StringPiece(str_ref.data,
str_ref.size), re);
- }
- } else {
- return Status::RuntimeError("Invalid pattern: {}",
pattern.debug_string());
+ std::unique_ptr<re2::RE2> regex;
+ std::unique_ptr<boost::regex> boost_regex;
+ RETURN_IF_ERROR(
+ compile_regex(re_pattern, state->enable_extended_regex,
®ex, &boost_regex));
+ auto sz = val.size();
+ for (size_t i = 0; i < sz; i++) {
+ RETURN_IF_ERROR(regex_search(regex.get(), boost_regex.get(),
val.get_data_at(i),
+ result.data() + i));
}
}
return Status::OK();
}
+Status FunctionLikeBase::compile_regex(std::string_view pattern, bool
enable_extended_regex,
+ std::unique_ptr<re2::RE2>* regex,
+ std::unique_ptr<boost::regex>*
boost_regex) {
+ RE2::Options opts;
+ opts.set_never_nl(false);
+ opts.set_dot_nl(true);
+ // A rejected pattern is reported through the returned Status (or handed
to Boost.Regex), and
+ // non-constant patterns are compiled once per row, so keep RE2 from
logging every rejection.
+ opts.set_log_errors(false);
+ *regex = std::make_unique<RE2>(re2::StringPiece(pattern.data(),
pattern.size()), opts);
+ if ((*regex)->ok()) {
+ return Status::OK();
+ }
+ if (!enable_extended_regex) {
+ return Status::InternalError(
+ "Invalid regex expression: {}. Error: {}. If you need advanced
regex features, "
+ "try setting enable_extended_regex=true",
+ pattern, (*regex)->error());
+ }
+
+ // RE2 failed, fallback to Boost.Regex
+ // This handles advanced regex features like zero-width assertions
+ regex->reset();
+ try {
+ *boost_regex =
+ std::make_unique<boost::regex>(pattern.data(), pattern.data()
+ pattern.size());
+ } catch (const boost::regex_error& e) {
+ return Status::InternalError("Invalid regex expression: {}. Error:
{}", pattern, e.what());
+ }
+ return Status::OK();
+}
+
+Status FunctionLikeBase::regex_search(const re2::RE2* regex, const
boost::regex* boost_regex,
+ const StringRef& val, unsigned char*
result) {
+ if (boost_regex != nullptr) {
+ try {
+ *result = boost::regex_search(val.data, val.data + val.size,
*boost_regex);
+ } catch (const boost::regex_error& e) {
+ return Status::InternalError("Failed to match regex expression:
{}. Error: {}",
+ boost_regex->str(), e.what());
+ }
+ return Status::OK();
+ }
+ *result = RE2::PartialMatch(re2::StringPiece(val.data, val.size), *regex);
+ return Status::OK();
+}
+
// hyperscan compile expression to database and allocate scratch space
bool FunctionLikeBase::should_fallback_to_re2(std::string_view regexp) {
return is_hyperscan_regexp_expensive(regexp);
@@ -1015,6 +1045,7 @@ Status FunctionRegexpLike::open(FunctionContext* context,
state->is_like_pattern = false;
state->search_state.enable_hyperscan_fallback =
context->state()->query_options().enable_hyperscan_fallback;
+ state->search_state.enable_extended_regex =
context->state()->enable_extended_regex();
state->function = regexp_fn;
state->scalar_function = regexp_fn_scalar;
if (context->is_col_constant(1)) {
@@ -1059,29 +1090,9 @@ Status FunctionRegexpLike::open(FunctionContext* context,
// reset hs_database to nullptr to indicate not use hyperscan
state->search_state.hs_database.reset();
state->search_state.hs_scratch.reset();
- RE2::Options opts;
- opts.set_never_nl(false);
- opts.set_dot_nl(true);
- state->search_state.regex = std::make_unique<RE2>(pattern_str,
opts);
- if (!state->search_state.regex->ok()) {
- if (!context->state()->enable_extended_regex()) {
- return Status::InternalError(
- "Invalid regex expression: {}. Error: {}. If
you need advanced "
- "regex features, try setting
enable_extended_regex=true",
- pattern_str,
state->search_state.regex->error());
- }
-
- // RE2 failed, fallback to Boost.Regex
- // This handles advanced regex features like zero-width
assertions
- state->search_state.regex.reset();
- try {
- state->search_state.boost_regex =
- std::make_unique<boost::regex>(pattern_str);
- } catch (const boost::regex_error& e) {
- return Status::InternalError("Invalid regex
expression: {}. Error: {}",
- pattern_str, e.what());
- }
- }
+ RETURN_IF_ERROR(compile_regex(
+ pattern_str, state->search_state.enable_extended_regex,
+ &state->search_state.regex,
&state->search_state.boost_regex));
}
state->function = constant_regex_fn;
state->scalar_function = constant_regex_fn_scalar;
diff --git a/be/src/exprs/function/like.h b/be/src/exprs/function/like.h
index 9759e7e563f..567f59ee1b6 100644
--- a/be/src/exprs/function/like.h
+++ b/be/src/exprs/function/like.h
@@ -185,6 +185,10 @@ struct LikeSearchState {
bool enable_hyperscan_fallback = true;
+ /// Whether a pattern rejected by RE2 may fall back to Boost.Regex,
mirrors the
+ /// `enable_extended_regex` session variable.
+ bool enable_extended_regex = false;
+
/// Used for LIKE predicates if the pattern is a constant argument, and is
either a
/// constant string or has a constant string at the beginning or end of
the pattern.
/// This will be set in order to check for that pattern in the
corresponding part of
@@ -366,6 +370,19 @@ protected:
static Status regexp_fn_scalar(const LikeSearchState* state, const
StringRef& val,
const StringRef& pattern, unsigned char*
result);
+ // Compile `pattern` with RE2. When RE2 rejects it and extended regex is
enabled, fall back
+ // to Boost.Regex, which supports zero-width assertions such as `(?=...)`
and `(?<=...)`.
+ // Exactly one of `regex` / `boost_regex` is set on success.
+ static Status compile_regex(std::string_view pattern, bool
enable_extended_regex,
+ std::unique_ptr<re2::RE2>* regex,
+ std::unique_ptr<boost::regex>* boost_regex);
+
+ // Match `val` with whichever engine `compile_regex` produced. Boost.Regex
can also fail while
+ // matching (e.g. the backtracking budget of a pathological pattern is
exhausted), which is
+ // reported as a non-OK Status instead of an exception.
+ static Status regex_search(const re2::RE2* regex, const boost::regex*
boost_regex,
+ const StringRef& val, unsigned char* result);
+
// hyperscan compile expression to database and allocate scratch space
static Status hs_prepare(FunctionContext* context, const char* expression,
hs_database_t** database, hs_scratch_t** scratch);
diff --git a/be/test/exprs/function/function_like_test.cpp
b/be/test/exprs/function/function_like_test.cpp
index 51a8a1ce56d..1543d519b99 100644
--- a/be/test/exprs/function/function_like_test.cpp
+++ b/be/test/exprs/function/function_like_test.cpp
@@ -20,6 +20,7 @@
#include <string>
#include <vector>
+#include "core/assert_cast.h"
#include "core/block/block.h"
#include "core/column/column_const.h"
#include "core/column/column_string.h"
@@ -43,11 +44,27 @@ public:
using FunctionLikeBase::should_fallback_to_re2;
};
+// How the pattern column reaches the function: a plain column, a const column
that is only
+// visible during execute(), or a const column already visible during open().
+enum class PatternMode { NON_CONST, CONST_AT_EXECUTE, CONST_AT_OPEN };
+
+static const char* pattern_mode_name(PatternMode mode) {
+ switch (mode) {
+ case PatternMode::NON_CONST:
+ return "non-constant pattern";
+ case PatternMode::CONST_AT_EXECUTE:
+ return "constant pattern during execute";
+ case PatternMode::CONST_AT_OPEN:
+ return "constant pattern during open";
+ }
+ return "";
+}
+
+// Runs `value <Function> pattern` for a single row with the given query
options and returns the
+// match result through `result`; the Status of open()/execute_impl() is
returned unchanged.
template <typename Function>
-Status execute_pattern_with_fallback_disabled(const std::string& value, const
std::string& pattern,
- bool constant_known_at_open) {
- TQueryOptions query_options;
- query_options.__set_enable_hyperscan_fallback(false);
+Status execute_single_pattern(const TQueryOptions& query_options, const
std::string& value,
+ const std::string& pattern, PatternMode mode,
uint8_t* result) {
RuntimeState runtime_state(query_options, TQueryGlobals {});
auto string_type = std::make_shared<DataTypeString>();
@@ -58,10 +75,12 @@ Status execute_pattern_with_fallback_disabled(const
std::string& value, const st
values->insert_data(value.data(), value.size());
auto patterns = ColumnString::create();
patterns->insert_data(pattern.data(), pattern.size());
- ColumnPtr pattern_column = ColumnConst::create(std::move(patterns), 1);
+ ColumnPtr pattern_column = mode == PatternMode::NON_CONST
+ ? ColumnPtr(std::move(patterns))
+ :
ColumnConst::create(std::move(patterns), 1);
std::vector<std::shared_ptr<ColumnPtrWrapper>> constant_columns(2);
- if (constant_known_at_open) {
+ if (mode == PatternMode::CONST_AT_OPEN) {
constant_columns[1] =
std::make_shared<ColumnPtrWrapper>(pattern_column);
}
context->set_constant_cols(constant_columns);
@@ -73,7 +92,31 @@ Status execute_pattern_with_fallback_disabled(const
std::string& value, const st
block.insert({std::move(values), string_type, "value"});
block.insert({std::move(pattern_column), string_type, "pattern"});
block.insert({nullptr, std::make_shared<DataTypeUInt8>(), "result"});
- return function.execute_impl(context.get(), block, {0, 1}, 2, 1);
+ RETURN_IF_ERROR(function.execute_impl(context.get(), block, {0, 1}, 2, 1));
+
+ // execute_impl always installs a ColumnUInt8 at the result position.
+ *result = assert_cast<const
ColumnUInt8&>(*block.get_by_position(2).column).get_element(0);
+ return Status::OK();
+}
+
+template <typename Function>
+Status execute_pattern_with_fallback_disabled(const std::string& value, const
std::string& pattern,
+ bool constant_known_at_open) {
+ TQueryOptions query_options;
+ query_options.__set_enable_hyperscan_fallback(false);
+ uint8_t result = 0;
+ return execute_single_pattern<Function>(
+ query_options, value, pattern,
+ constant_known_at_open ? PatternMode::CONST_AT_OPEN :
PatternMode::CONST_AT_EXECUTE,
+ &result);
+}
+
+Status execute_regexp_with_extended_regex(const std::string& value, const
std::string& pattern,
+ bool enable_extended_regex,
PatternMode mode,
+ uint8_t* result) {
+ TQueryOptions query_options;
+ query_options.__set_enable_extended_regex(enable_extended_regex);
+ return execute_single_pattern<FunctionRegexpLike>(query_options, value,
pattern, mode, result);
}
template <typename Function>
@@ -315,6 +358,53 @@ TEST(FunctionLikeTest, regexp_empty_constant_pattern) {
check_constant_pattern_batch<FunctionRegexpLike>({"abc", ""}, "", {1, 1});
}
+TEST(FunctionLikeTest, regexp_extended_regex_all_pattern_modes) {
+ // Lookaround assertions are rejected by both Hyperscan and RE2, so they
only work through
+ // the Boost.Regex fallback, which must be reachable no matter how the
pattern is supplied.
+ struct Case {
+ std::string value;
+ std::string pattern;
+ uint8_t expected;
+ };
+ const std::vector<Case> cases = {{"foo123bar", "foo(?=123)", 1},
+ {"foo124bar", "foo(?=123)", 0},
+ {"foobar", "(?<=foo)bar", 1},
+ {"fobar", "(?<=foo)bar", 0}};
+
+ for (auto mode :
+ {PatternMode::NON_CONST, PatternMode::CONST_AT_EXECUTE,
PatternMode::CONST_AT_OPEN}) {
+ SCOPED_TRACE(pattern_mode_name(mode));
+ for (const auto& c : cases) {
+ SCOPED_TRACE(c.value + " REGEXP " + c.pattern);
+ uint8_t result = 0;
+ auto status =
+ execute_regexp_with_extended_regex(c.value, c.pattern,
true, mode, &result);
+ EXPECT_TRUE(status.ok()) << status.to_string();
+ EXPECT_EQ(result, c.expected);
+
+ status = execute_regexp_with_extended_regex(c.value, c.pattern,
false, mode, &result);
+ EXPECT_FALSE(status.ok());
+ EXPECT_NE(status.to_string().find("enable_extended_regex"),
std::string::npos)
+ << status.to_string();
+ }
+
+ // A pattern that is invalid for Boost.Regex too still fails.
+ uint8_t result = 0;
+ auto status = execute_regexp_with_extended_regex("abc", "(", true,
mode, &result);
+ EXPECT_FALSE(status.ok());
+ EXPECT_NE(status.to_string().find("Invalid regex expression"),
std::string::npos)
+ << status.to_string();
+
+ // Boost.Regex gives up on a pathological pattern while matching; that
must surface as a
+ // Status, not as an exception escaping the function.
+ status = execute_regexp_with_extended_regex(std::string(60, 'a'),
"(?=a)(a+)+b", true, mode,
+ &result);
+ EXPECT_FALSE(status.ok());
+ EXPECT_NE(status.to_string().find("Failed to match regex expression"),
std::string::npos)
+ << status.to_string();
+ }
+}
+
TEST(FunctionLikeTest, regexp_constant_substring_boundaries) {
check_constant_pattern_batch<FunctionRegexpLike>({"ab", "c", "", "abc",
"", "xabcx"}, "abc",
{0, 0, 0, 1, 0, 1});
diff --git
a/regression-test/data/query_p0/sql_functions/string_functions/test_regexp_extended_nonconstant_pattern.out
b/regression-test/data/query_p0/sql_functions/string_functions/test_regexp_extended_nonconstant_pattern.out
new file mode 100644
index 00000000000..5511ea0c942
--- /dev/null
+++
b/regression-test/data/query_p0/sql_functions/string_functions/test_regexp_extended_nonconstant_pattern.out
@@ -0,0 +1,28 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !constant_pattern --
+1 foobar true false
+2 fobar false false
+3 foo123bar false true
+4 foo124bar false false
+5 foobar true false
+6 \N \N \N
+7 foobar true false
+
+-- !column_pattern --
+1 foobar (?<=foo)bar true true true
+2 fobar (?<=foo)bar false false false
+3 foo123bar foo(?=123) true true true
+4 foo124bar foo(?=123) false false false
+5 foobar ^foo true true true
+6 \N foo(?=123) \N \N \N
+7 foobar \N \N \N \N
+
+-- !column_pattern_filter --
+1
+3
+5
+
+-- !column_pattern_not --
+2
+4
+
diff --git
a/regression-test/suites/query_p0/sql_functions/string_functions/test_regexp_extended_nonconstant_pattern.groovy
b/regression-test/suites/query_p0/sql_functions/string_functions/test_regexp_extended_nonconstant_pattern.groovy
new file mode 100644
index 00000000000..6f7fb88fde8
--- /dev/null
+++
b/regression-test/suites/query_p0/sql_functions/string_functions/test_regexp_extended_nonconstant_pattern.groovy
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_regexp_extended_nonconstant_pattern") {
+ sql "DROP TABLE IF EXISTS test_regexp_extended_nonconstant_pattern"
+ sql """
+ CREATE TABLE test_regexp_extended_nonconstant_pattern (
+ id INT,
+ s VARCHAR(32),
+ p VARCHAR(64)
+ )
+ DUPLICATE KEY(id)
+ DISTRIBUTED BY HASH(id) BUCKETS 1
+ PROPERTIES ("replication_num" = "1")
+ """
+ sql """
+ INSERT INTO test_regexp_extended_nonconstant_pattern VALUES
+ (1, 'foobar', '(?<=foo)bar'),
+ (2, 'fobar', '(?<=foo)bar'),
+ (3, 'foo123bar', 'foo(?=123)'),
+ (4, 'foo124bar', 'foo(?=123)'),
+ (5, 'foobar', '^foo'),
+ (6, NULL, 'foo(?=123)'),
+ (7, 'foobar', NULL)
+ """
+
+ sql "SET enable_extended_regex = true"
+ // Lookaround patterns work the same whether they are constants or read
from a column.
+ order_qt_constant_pattern """
+ SELECT id, s, s REGEXP '(?<=foo)bar', REGEXP(s, 'foo(?=123)')
+ FROM test_regexp_extended_nonconstant_pattern
+ """
+ order_qt_column_pattern """
+ SELECT id, s, p, s REGEXP p, REGEXP(s, p), s RLIKE p
+ FROM test_regexp_extended_nonconstant_pattern
+ """
+ order_qt_column_pattern_filter """
+ SELECT id FROM test_regexp_extended_nonconstant_pattern WHERE s REGEXP
p
+ """
+ order_qt_column_pattern_not """
+ SELECT id FROM test_regexp_extended_nonconstant_pattern WHERE NOT s
REGEXP p
+ """
+ // A column pattern that Boost.Regex rejects as well ('(' from substr) is
still an error.
+ test {
+ sql """
+ SELECT id, s REGEXP substr(p, 1, 1) FROM
test_regexp_extended_nonconstant_pattern
+ WHERE id = 1
+ """
+ exception "Invalid regex expression: ("
+ }
+ // Boost.Regex exhausts its backtracking budget on this column pattern;
the query must fail
+ // with an error instead of crashing the backend.
+ test {
+ sql """
+ SELECT id FROM test_regexp_extended_nonconstant_pattern
+ WHERE repeat('a', 60) REGEXP concat(substr(p, 1, 0), '(?=a)(a+)+b')
+ """
+ exception "Failed to match regex expression"
+ }
+
+ sql "SET enable_extended_regex = false"
+ // Without the session variable, column patterns report the same error as
constant ones.
+ test {
+ sql "SELECT id, s REGEXP p FROM
test_regexp_extended_nonconstant_pattern ORDER BY id"
+ exception "try setting enable_extended_regex=true"
+ }
+ test {
+ sql "SELECT id, s REGEXP '(?<=foo)bar' FROM
test_regexp_extended_nonconstant_pattern ORDER BY id"
+ exception "try setting enable_extended_regex=true"
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]