Applies to VAST Cluster 5.5.
Functions › Regular expressions › Regular expressions. regexp_matches(s, pattern) is true if the regular expression pattern matches any part of s. Anchor the pattern with ^ and $ to test the whole string, or use regexp_full_match.
Syntax
regexp_matches(s, pattern)
regexp_matches(s, pattern, options)
| Argument | Type | Description |
|---|---|---|
s |
VARCHAR |
The string to test. |
pattern |
VARCHAR |
The regular expression (syntax). A constant or a column. |
options |
VARCHAR |
Optional, a constant. Flags such as 'i' (ignore case), 's' (. matches a line break) or 'l' (literal text); see options. |
Returns: BOOLEAN.
Set up the examples
The examples connect with the ADBC driver, as in VAST Query Engine (ADBC), and work in a schema called docs_sql_fn_string_regexp_matches. Set QE_ENDPOINT, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and VASTDB_BUCKET (SQL reference), and run the blocks in order in one Python session. Below each block is the output you should see.
This block connects, defines the run() and plan() helpers, and creates a table of five log lines. Line 4 is empty, and the last row has no values:
import os
import re
import adbc_driver_manager
import adbc_driver_manager.dbapi as dbapi
import adbc_driver_vastdb
BUCKET = os.environ["VASTDB_BUCKET"]
SCHEMA = f'"{BUCKET}/docs_sql_fn_string_regexp_matches"'
conn = dbapi.connect(
driver=adbc_driver_vastdb.get_driver_path(),
db_kwargs={
"vast.db.endpoint": os.environ["QE_ENDPOINT"],
"vast.db.access_key": os.environ["AWS_ACCESS_KEY_ID"],
"vast.db.secret_key": os.environ["AWS_SECRET_ACCESS_KEY"],
},
autocommit=True,
)
def server_message(e):
"""The server's message in a driver error, without the request ID or the echoed statement."""
text = re.sub(r"\[req=0x[0-9a-f]+\]: ", "", str(e))
found = (re.search(r'message: "(.*?)", request_id', text, re.S)
or re.search(r"description: (.*)\)", text, re.S))
message = found.group(1) if found else text.strip().splitlines()[-1]
message = re.sub(r"^Plan query: '.*?' failed with: ", "", message, flags=re.S)
message = re.split(r"\\n|\n", message)[0].replace('\\"', '"')
return message.replace(BUCKET, "<bucket>")
def run(sql):
"""Run one statement. Print the rows it returns, or the error."""
try:
with conn.cursor() as cur:
cur.execute(sql)
rows = cur.fetchall() if cur.description else []
except (adbc_driver_manager.Error, OSError) as e:
print(f"{type(e).__name__}: {server_message(e)}")
else:
print(rows)
def plan(sql):
"""Print the plan without its ~N Rows lines: the table's row count, which can still be 0 just after an insert."""
with conn.cursor() as cur:
cur.execute("EXPLAIN " + sql)
steps = cur.fetchall()[0][1]
print("\n".join(line for line in steps.splitlines() if "Rows" not in line))
TABLE = f"{SCHEMA}.logs"
run(f"CREATE SCHEMA IF NOT EXISTS {SCHEMA}")
run(f"CREATE TABLE IF NOT EXISTS {TABLE} (id BIGINT, line VARCHAR)")
run(f"INSERT INTO {TABLE} VALUES "
"(1, 'ERROR 2026-10-16 disk full'), "
"(2, 'warn: Disk 93% full'), "
"(3, 'INFO 2026-10-17 ok'), "
"(4, ''), "
"(5, NULL)")
[]
[]
[(5,)]
What it does
regexp_matches searches s for the first place where pattern matches, and returns true if there is one:
| Call | Result | Why |
|---|---|---|
regexp_matches('abc', 'b') |
true |
b matches in the middle |
regexp_matches('abc', '^b') |
false |
^ anchors the pattern at the start |
regexp_matches('abc', '^abc$') |
true |
anchored at both ends: the whole string |
regexp_matches('ABC', 'b', 'i') |
true |
option i ignores case |
regexp_matches('axb', 'a.b', 'l') |
false |
option l: the pattern is literal text |
The pattern syntax and the options are described on Regular-expression syntax and options. A . matches one code point, so e followed by a combining accent (U+0301) needs .., and 'cafe\u0301' doesn't match 'é$'.
NULLs and edge cases
| Input | Result |
|---|---|
A NULL string or pattern |
NULL |
A NULL options string |
Error: Regex options field must not be NULL |
An empty pattern: regexp_matches('abc', ''), regexp_matches('', '') |
true |
regexp_matches('', '.') |
false |
regexp_matches('ABC', 'b', 'ic') (the last of i and c wins) |
false |
regexp_matches('abc', 'b', 'g') |
Error: Option 'g' (global replace) is only valid for regexp_replace |
An invalid constant pattern: regexp_matches('abc', '(') |
Error: missing ): ( |
A number, date, boolean, BLOB or list |
Error: No function matches the given name and argument types, for example 'regexp_matches(INTEGER_LITERAL, STRING_LITERAL)' |
The same cases, as the cluster returns them. They use the table and helpers from Set up the examples:
run("SELECT regexp_matches(NULL, 'a'), regexp_matches('abc', NULL), regexp_matches('abc', ''), "
"regexp_matches('', ''), regexp_matches('', '.'), regexp_matches('ABC', 'b', 'ic')")
run("SELECT regexp_matches('abc', 'b', NULL)")
run("SELECT regexp_matches('abc', 'b', 'g')")
run("SELECT regexp_matches('abc', '(')")
run("SELECT regexp_matches(123, '1')")
[(None, None, True, True, False, False)]
InternalError: Invalid Input Error: Regex options field must not be NULL
InternalError: Invalid Input Error: Option 'g' (global replace) is only valid for regexp_replace
InternalError: Invalid Input Error: missing ): (
InternalError: Binder Error: No function matches the given name and argument types 'regexp_matches(INTEGER_LITERAL, STRING_LITERAL)'. You might need to add explicit type casts.
Examples
Which lines hold a date, which mention a disk in any case, and which start with an upper-case word followed by a space:
run(f"SELECT id, regexp_matches(line, '\\d{{4}}-\\d{{2}}-\\d{{2}}'), regexp_matches(line, 'disk', 'i'), "
f"regexp_matches(line, '^[A-Z]+ ') FROM {TABLE} ORDER BY id LIMIT 10")
[(1, True, True, True), (2, False, True, False), (3, True, False, True), (4, False, False, False), (5, None, None, None)]
Keep only the error and warning lines, in any case:
run(f"SELECT id, line FROM {TABLE} WHERE regexp_matches(line, '^(error|warn)', 'i') ORDER BY id LIMIT 10")
[(1, 'ERROR 2026-10-16 disk full'), (2, 'warn: Disk 93% full')]
How it runs in VAST
The Query Engine runs on the cluster's CNodes, and a query on a table starts with a scan in VAST DataBase (VAST_SCAN). In WHERE, the Query Engine first rewrites simple patterns into cheaper tests:
| Pattern | Becomes | Runs in |
|---|---|---|
'^literal$' (the whole string, no special characters) |
line = 'literal' |
VAST DataBase (pushed down; no FILTER step) |
'^literal' |
prefix(line, 'literal') |
a FILTER step in the Query Engine |
'literal$' |
suffix(line, 'literal') |
a FILTER step |
'literal' |
contains(line, 'literal') |
a FILTER step |
anything else, with option i, or from a column |
regexp_matches(...) |
a FILTER step |
In a FILTER step, the scan sends the column for every row and the Query Engine keeps the rows that match. In the SELECT list, regexp_matches is computed for each row the scan returns. The plans of a whole-string pattern, a prefix and a general pattern:
plan(f"SELECT id FROM {TABLE} WHERE regexp_matches(line, '^INFO 2026-10-17 ok$')")
plan(f"SELECT id FROM {TABLE} WHERE regexp_matches(line, '^ERROR')")
plan(f"SELECT id FROM {TABLE} WHERE regexp_matches(line, '\\d{{4}}-\\d{{2}}')")
┌───────────────────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ Projections: id │
│ │
└───────────────────────────┘
┌───────────────────────────┐
│ PROJECTION │
│ ──────────────────── │
│ #1 │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ FILTER │
│ ──────────────────── │
│ prefix(line, 'ERROR') │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ │
│ Projections: │
│ line │
│ id │
│ │
└───────────────────────────┘
┌───────────────────────────┐
│ PROJECTION │
│ ──────────────────── │
│ #1 │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ FILTER │
│ ──────────────────── │
│ regexp_matches(line, ' │
│ \d{4}-\d{2}') │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ │
│ Projections: │
│ line │
│ id │
│ │
└───────────────────────────┘
Make it faster
- Prefer a literal or anchored pattern.
'^ERROR'runs asprefix()and'disk'ascontains(), which are cheaper than a general pattern. A whole-string literal is pushed down. - Use a range instead of a prefix.
line >= 'ERROR' AND line < 'ERROS'selects the lines that start withERROR, and it's pushed down to VAST DataBase;regexp_matches(line, '^ERROR')isn't. The plan has noFILTERstep:
plan(f"SELECT id FROM {TABLE} WHERE line >= 'ERROR' AND line < 'ERROS'")
┌───────────────────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ Projections: id │
│ │
└───────────────────────────┘
- Combine it with a pushed-down condition. In
id > 100 AND regexp_matches(line, 'disk'),id > 100is still evaluated in VAST DataBase, so the pattern runs only on the rows that pass it. - Extract at load time. If you filter on a part of the line often, such as the level, store it in its own column with regexp_extract when you write the data.
Limitations and errors
- Strings only. A number, date, boolean,
BLOBor list fails withNo function matches the given name and argument types. Cast the value toVARCHARfirst. - The options string is a constant and isn't
NULL. From a column it fails withRegex options field must be a constant;NULLfails withRegex options field must not be NULL. - A pattern from a column is checked row by row. One invalid value fails the query with the reason, such as
missing ): l+(ortrailing \. - Some pattern features aren't available, such as backreferences and lookahead: see syntax and options.
- Other dialects' names aren't available.
regexp_likeandrlikefail withScalar Function with name ... does not exist!;s REGEXP pands RLIKE pfail with a syntax error. Useregexp_matches.
run(f"SELECT regexp_matches(line, 'a', CAST(id AS VARCHAR)) FROM {TABLE} LIMIT 1")
run("SELECT regexp_like('abc', 'b')")
run("SELECT 'abc' REGEXP 'b'")
InternalError: Invalid Input Error: Regex options field must be a constant
InternalError: Catalog Error: Scalar Function with name regexp_like does not exist!
InternalError: Parse query: 'SELECT 'abc' REGEXP 'b'' failed with: Parser Error: syntax error at or near "'b'"
Similar functions
| Function | Tests | Result for 'xabcx' and abc |
|---|---|---|
regexp_matches('xabcx', 'abc') |
a regular expression, anywhere | true |
regexp_full_match('xabcx', 'abc'), ~ |
a regular expression, the whole string | false |
SIMILAR TO 'abc' |
a regular expression, the whole string | false |
LIKE '%abc%' |
% and _ wildcards, the whole string |
true |
contains('xabcx', 'abc') |
plain text, anywhere | true |
run("SELECT regexp_matches('xabcx', 'abc'), regexp_full_match('xabcx', 'abc'), 'xabcx' ~ 'abc', "
"'xabcx' SIMILAR TO 'abc', 'xabcx' LIKE '%abc%', contains('xabcx', 'abc')")
[(True, False, False, False, True, True)]
Clean up:
run(f"DROP TABLE IF EXISTS {TABLE}")
run(f"DROP SCHEMA IF EXISTS {SCHEMA}")
conn.close()
[]
[]
See also
- Regular-expression syntax and options.
- Regular expressions: all the regular-expression functions.
- Where filters run and pushdowns.
- EXPLAIN: how to read a plan.