Skip to content

fix(oracle): honour the n and m match parameters in REGEXP_LIKE - #2045

Open
btlqql wants to merge 1 commit into
IvorySQL:masterfrom
btlqql:btlqql-regexp-like-matchparam
Open

fix(oracle): honour the n and m match parameters in REGEXP_LIKE#2045
btlqql wants to merge 1 commit into
IvorySQL:masterfrom
btlqql:btlqql-regexp-like-matchparam

Conversation

@btlqql

@btlqql btlqql commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Defect

REGEXP_LIKE is implemented by sys.regexp_like(...) -> ora_regexp_like() in
contrib/ivorysql_ora/src/builtin_functions/character_datatype_functions.c. It parses the
match_parameter argument with its own switch, while the other five REGEXP_* functions in the
same file go through ora_parse_re_flags() (src/backend/utils/adt/regexp.c). The two have drifted
apart, so the Oracle option letters do not mean what Oracle documents:

	/* parse flag options */
	out_flag = REG_ADVANCED;
	for (i = 0; i <in_flag_len; i++)
	{
		switch (in_flag_p[i])
		{
			case 'i':
				out_flag  |=  REG_ICASE;
				break;
			case 'n':
				out_flag |= REG_NEWLINE;      /* <-- PG newline-sensitive mode */
				break;
			case 'c':
				out_flag  &=  ~REG_ICASE;
				break;
			case 'x':
				out_flag |= REG_EXPANDED;
				break;
			default:
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("invalid option of regexp_like: %c",
						 in_flag_p[i])));
				break;
		}
	}

(ora_regexp_like() lines 791-816 before the patch; ora_regexp_like_no_flags() had the same
out_flag = REG_ADVANCED; two lines below.)

Three consequences:

  1. 'm' falls into default: -> the documented Oracle option raises an error.
  2. 'n' sets PostgreSQL's REG_NEWLINE (REG_NLSTOP | REG_NLANCH), which excludes a newline
    from "." and turns on the multiline anchors. Oracle's 'n' is the opposite: it allows "."
    to match a newline, and only 'm' may turn on the anchors.
  3. With no match_parameter, "." matches a newline; Oracle only permits that with 'n'.
    ora_regexp_like_no_flags() (the two-argument registration) is wrong for the same reason.

How I triggered it

sys.regexp_like is the C function behind the package's REGEXP_LIKE; in Oracle-compatible mode the
unqualified name reaches it too (that is what the neighbouring REGEXP_COUNT/REGEXP_SUBSTR cases
in the same regression file rely on), and the qualified form is unambiguous either way:

-- raises an error today, Oracle returns FALSE
select sys.regexp_like('X' || chr(10), 'X.', 'm') from dual;
ERROR:  invalid option of regexp_like: m

-- '.' must not match a newline without 'n'; Oracle returns FALSE
select sys.regexp_like('X' || chr(10), 'X.') from dual;          -- t today

-- 'n' must not enable the multiline anchors; Oracle returns FALSE
select sys.regexp_like('x' || chr(10) || 'b' || chr(10) || 'y',
                       '^b$', 'n') from dual;                    -- t today

The expected values are not invented: the same file already pins the identical n/m matrix for
REGEXP_COUNT, REGEXP_INSTR, REGEXP_SUBSTR and REGEXP_REPLACE at 0|0|1|0 (dot vs. newline)
and 0|0|0|1 (multiline anchors), and the test below reproduces exactly those two rows for
REGEXP_LIKE.

Fix

Parse the flags the way the rest of the family does (character_datatype_functions.c, 2 hunks, 19
lines): both entry points start from REG_ADVANCED | REG_NLDOT (Oracle's default: "." does not
match a newline), 'n' clears REG_NLDOT and 'm' sets REG_NLANCH. The unknown-option error
message is unchanged, so the existing ERROR: invalid option of regexp_like: p expectation stays
valid.

Regression test

contrib/ivorysql_ora/sql/ora_character_datatype_functions.sql + the matching expected file: two
statements appended to the existing match-parameter matrix, using the same chr(10) idiom and the
same column aliases as the neighbouring cases (so the expected table widths are identical to rows
that are already in the file).

What I could not verify

There is no C toolchain, make, bison/flex, perl or container in my environment, so I could
not build IvorySQL or run oracle_regression/contrib_regression locally. The patch and the
expected output are unverified by execution
- CI must run contrib/ivorysql_ora's
ora_regression (the ora_character_datatype_functions test) to confirm. Everything else was
checked statically against this tree: REG_NLDOT/REG_NLANCH come from src/include/regex/regex.h
(already included by the file, and REG_NLANCH is already used in ora_regexp_count),
ora_parse_re_flags() in src/backend/utils/adt/regexp.c is the intended semantics
('n' -> clear REG_NLDOT, 'm' -> set REG_NLANCH), and the expected numbers were taken from the
verified REGEXP_COUNT block for the same expressions.

Fixes #2044

Summary by CodeRabbit

  • Bug Fixes
    • Updated regular expression matching so sys.regexp_like consistently handles newline and multiline options.
    • The n option allows dots to match newline characters, while m enables multiline ^ and $ anchors.
    • Default matching behavior now excludes newline characters from dot matches.

sys.regexp_like() parsed its match_parameter argument with a hand-rolled
switch (contrib/ivorysql_ora/src/builtin_functions/character_datatype_functions.c,
ora_regexp_like()) that had drifted away from ora_parse_re_flags(), the
helper the other five REGEXP_* functions in the same file already use:

  - 'm' was not handled at all, so the documented Oracle option raised
    ERROR: invalid option of regexp_like: m
  - 'n' set REG_NEWLINE, i.e. PostgreSQL's newline-sensitive mode, which
    excludes a newline from "." and turns on the multiline anchors.  That
    is the opposite of Oracle's 'n', which *allows* "." to match a
    newline, and it also silently enabled an option that only 'm' may turn
    on.
  - with no match_parameter at all "." matched a newline, although Oracle
    only permits that with 'n' (ora_regexp_like_no_flags() had the same
    problem, so the two-argument form was wrong as well).

The same switch is also the reason REGEXP_LIKE behaved unlike REGEXP_COUNT,
REGEXP_INSTR, REGEXP_SUBSTR and REGEXP_REPLACE, whose 'n'/'m' matrix is
already pinned by ora_character_datatype_functions.

Fix: parse the flags the way ora_parse_re_flags() does.  'n' now clears
REG_NLDOT, 'm' now sets REG_NLANCH, and both entry points start from
REG_ADVANCED | REG_NLDOT (Oracle's default: "." does not match a newline).
The unknown-option error message is unchanged.

Trigger (before the fix, sys.regexp_like is the C function behind the
package's REGEXP_LIKE and is reachable both qualified and, in Oracle mode,
unqualified):

  select sys.regexp_like('X' || chr(10), 'X.', 'm') from dual;
  ERROR:  invalid option of regexp_like: m

  select sys.regexp_like('X' || chr(10), 'X.');       -- Oracle: false
  select sys.regexp_like('X' || chr(10), 'X.', 'n');  -- Oracle: true

Regression test: extend the existing n/m matrix in
contrib/ivorysql_ora/sql/ora_character_datatype_functions.sql (and the
matching expected file) with the REGEXP_LIKE rows.  The expected values
(0|0|1|0 and 0|0|0|1) are the same ones the neighbouring REGEXP_COUNT and
REGEXP_SUBSTR cases produce for the identical expressions.

Signed-off-by: btlqql <2977859784@qq.com>
@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 455fa584-1ede-491e-9e7f-b3f6e4397223

📥 Commits

Reviewing files that changed from the base of the PR and between 03b24b1 and d6b6bc7.

📒 Files selected for processing (3)
  • contrib/ivorysql_ora/expected/ora_character_datatype_functions.out
  • contrib/ivorysql_ora/sql/ora_character_datatype_functions.sql
  • contrib/ivorysql_ora/src/builtin_functions/character_datatype_functions.c

Included review availability: Your plan provides up to 4 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

sys.regexp_like now follows the Oracle-compatible n and m match-parameter semantics. Default behavior excludes newlines from ., and regression tests cover newline matching and multiline anchors.

Changes

REGEXP_LIKE flag behavior

Layer / File(s) Summary
Flag parsing and defaults
contrib/ivorysql_ora/src/builtin_functions/character_datatype_functions.c
ora_regexp_like excludes newlines from . by default, uses n to allow newline matching, and uses m for multiline anchors. The no-flags variant uses the same default.
Regression coverage
contrib/ivorysql_ora/sql/ora_character_datatype_functions.sql, contrib/ivorysql_ora/expected/ora_character_datatype_functions.out
Tests verify default, c, n, and m results for newline matching and multiline anchors.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: ai-yang

Merge Risk: ⚪ Minimal · up to d6b6b

The updated REGEXP_LIKE behavior is covered for newline and multiline-anchor handling, with no actionable merge risk identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: fixing Oracle-compatible handling of the n and m match parameters in REGEXP_LIKE.
Linked Issues check ✅ Passed Issue #2044 coding requirements are met. ora_regexp_like uses REG_ADVANCED | REG_NLDOT by default, clears REG_NLDOT for n, and sets REG_NLANCH for m. ora_regexp_like_no_flags uses the co…
Out of Scope Changes check ✅ Passed The reported changes stay within Issue #2044. They modify the two REGEXP_LIKE entry points and add focused regression coverage for their match-parameter semantics. No unrelated production behavior o…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files. (2 skipped: 2 …
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

REGEXP_LIKE rejects the documented 'm' match parameter and misreads 'n'

1 participant