oracle/graal · error · UnsupportedRegexException

nested quantifier in regular expression

Error message

nested quantifier in regular expression

What it means

Thrown by the OracleDB-flavor parser when a quantifier token immediately follows another quantifier token. Oracle's SQL regex dialect does not allow stacked quantifiers (no possessive or lazy modifiers on quantifiers), so prevKind == Token.Kind.quantifier triggers UnsupportedRegexException(NESTED_QUANTIFIER).

Source

Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/oracledb/OracleDBRegexParser.java:178

                    astBuilder.addDollar();
                    astBuilder.nextSequence();
                    astBuilder.pushLookAheadAssertion(false);
                    astBuilder.addCharClass(CodePointSet.create('\n'));
                    if (token.kind == Token.Kind.Z || !flags.isMultiline()) {
                        astBuilder.addDollar();
                    }
                    astBuilder.popGroup();
                    if (token.kind == Token.Kind.dollar && flags.isMultiline()) {
                        astBuilder.addPositionAssertion(PositionAssertion.Type.MATCH_END);
                    }
                    astBuilder.popGroup();
                    break;
                case backReference:
                    astBuilder.addBackReference((Token.BackReference) token, flags.isIgnoreCase());
                    break;
                case quantifier:
                    if (prevKind == Token.Kind.quantifier) {
                        throw new UnsupportedRegexException(OracleDBErrorMessages.NESTED_QUANTIFIER);
                    }
                    if (astBuilder.getCurTerm() == null || prevKind == Token.Kind.captureGroupBegin) {
                        // quantifiers without target are ignored
                        break;
                    }
                    astBuilder.addQuantifier((Token.Quantifier) token);
                    break;
                case alternation:
                    astBuilder.nextSequence();
                    break;
                case captureGroupBegin:
                    if (lexer.numberOfCaptureGroupsSoFar() <= 10) {
                        // oracledb only tracks capture groups 0 - 9
                        astBuilder.pushCaptureGroup(token);
                    } else {
                        astBuilder.pushGroup(token);
                    }
                    break;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Replace the two-letter quantifier forms with the plain greedy form: 'a*?' -> 'a*', 'a++' -> 'a+'
  2. Wrap the inner quantified expression in a group if you need repetition of a repetition: 'a**' is invalid, '(a*)*' is the closest legal form (mind the semantics)
  3. Strip lazy/possessive suffixes when translating patterns from other dialects into OracleDB flavor

Example fix

-- before (OracleDB flavor)
a.*?b  -- lazy quantifier

-- after
a.*b   -- greedy quantifier
Defensive patterns

Strategy: try-catch

Validate before calling

boolean hasStackedQuantifiers(String pattern) {
    return java.util.regex.Pattern.compile("([*+?]|\\{[0-9]+(,[0-9]*)?\\})([*+?]|\\{)").matcher(pattern).find();
}

Try / catch

try {
    RegexObject re = compileOracleDB(pattern);
} catch (UnsupportedRegexException e) {
    if (e.getReason().contains("nested quantifier")) {
        // drop lazy/possessive second quantifier and retry once
    } else { throw e; }
}

Prevention

When it happens

Trigger: Compiling an OracleDB-flavor regex such as 'a**', 'a++', 'a*?', '(ab)+?' or 'a{2,3}*' — any pattern where a quantifier directly follows another quantifier token. The parser sees the second quantifier with prevKind == quantifier and throws.

Common situations: Porting PCRE/Java patterns with lazy ('*?') or possessive ('*+', '++') quantifiers into Oracle Database regex semantics (e.g. via a GraalVM-based SQL engine using TRegex's OracleDB flavor); JS/Python-style regexes pasted into SQL REGEXP_LIKE calls.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/7a4d5eac6fe4cbe1. Report an issue: GitHub.