Skip to content

[BUG] JSQLParser 5.4-SNAPSHOT : PostgreSQL : LIKE ANY (ARRAY[...]) / LIKE ALL (ARRAY[...]) fails to parse #2432

@midnightnnn

Description

@midnightnnn

Failing SQL Feature

PostgreSQL LIKE / ILIKE combined with ANY / ALL over an array constructor cannot be parsed:

  • col LIKE ANY (ARRAY['%a%', '%b%'])
  • col ILIKE ALL (ARRAY['%a%', '%b%'])

This is the standard PostgreSQL idiom for matching a column against multiple patterns in a single predicate (PostgreSQL docs:
9.7.1. LIKE +
9.24. Row and Array Comparisons). The parser bails on the ( immediately after ANY / ALL, because there is no grammar branch that allows an ARRAY[...] constructor on the right-hand side of LIKE.

SQL Example

Minimal failing input:

SELECT *
FROM t
WHERE col LIKE ANY (ARRAY['%a%', '%b%']);

Also fails:

SELECT *
FROM t
WHERE col ILIKE ANY (ARRAY['%a%', '%b%']);

SELECT *
FROM t
WHERE col LIKE ALL (ARRAY['%a%', '%b%']);

A more realistic example, where the predicate sits inside a CASE expression in the SELECT list (this is the exact shape that originally tripped the parser for us):

SELECT
  CASE
    WHEN category = 'stock'
         AND transaction_type LIKE ANY (ARRAY['%deposit%', '%inflow%', '%dividend%'])
    THEN 1
    ELSE 0
  END AS inflow_count
FROM transactions;

Observed error:

net.sf.jsqlparser.parser.ParseException:
  Encountered unexpected token: "(" "(" at line 1, column ...
  Was expecting one of: "." "::" "THEN" "[" "^"

Reproduction online (JSQLFormatter, latest SNAPSHOT): <paste the http://jsqlformatter.manticore-projects.com link here after pasting the SQL above>

Software Information

  • JSqlParser: 5.4-SNAPSHOT (master, commit 2b141568, 2026-04-12). Also reproduced on 4.9.
  • Database: PostgreSQL.

Grammar references

Checked against the latest SNAPSHOT grammar at src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt (and equivalently the published Syntax Diagram).

1. LikeExpression rhs is restricted to SimpleExpression() — lines 7036–7073, in particular line 7059:

Expression LikeExpression(Expression leftExpression) #LikeExpression:
{ ... }
{
    [<K_NOT> { result.setNot(true); } ]
    (
        token = <K_LIKE>
        | token = <K_ILIKE>
        | token = <K_RLIKE>
        | token = <K_REGEXP_LIKE>
        | token = <K_REGEXP>
        | token = <K_SIMILAR_TO>
        | token = <K_MATCH_ANY>
        | token = <K_MATCH_ALL>
        | token = <K_MATCH_PHRASE>
        | token = <K_MATCH_PHRASE_PREFIX>
        | token = <K_MATCH_REGEXP>
    ) { result.setLikeKeyWord( LikeExpression.KeyWord.from(token.image)); }
    [ LOOKAHEAD(2) <K_BINARY> {result.setUseBinary(true); } ]
    rightExpression = SimpleExpression()                       // <-- line 7059
    ...
}

LikeExpression never dispatches to AnyComparisonExpression, so the ANY / SOME / ALL keyword on the right-hand side cannot be picked up at all in this position.

2. AnyComparisonExpression only accepts a parenthesized SELECT — lines 7389–7406, in particular line 7402:

Expression AnyComparisonExpression() :
{ AnyType anyType; Select select; }
{
    (
        <K_ANY>  { anyType = AnyType.ANY; }
        | <K_SOME> { anyType = AnyType.SOME; }
        | <K_ALL>  { anyType = AnyType.ALL; }
    )
    select = ParenthesedSelect()                               // <-- line 7402
    {
      return new AnyComparisonExpression(anyType, select);
    }
}

So even if LikeExpression did route through AnyComparisonExpression, the array form (ARRAY[...]) would still be rejected — only a (SELECT ...) is accepted today.

What does already work, and why it doesn't help here

There is one ANY(ARRAY...) test case on master,
src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java:100-107:

@Test
void testParenthesisCastIssue1997() throws JSQLParserException {
    String sqlStr = "SELECT ((foo)::text = ANY((ARRAY['bar'])::text[]))";
    assertSqlCanBeParsedAndDeparsed(sqlStr, true);

    sqlStr = "SELECT ((foo)::text = ANY((((ARRAY['bar'])))::text[]))";
    assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}

This passes only because ANY is here on the right-hand side of =, where it can be parsed as a function name applied to a fully parenthesized cast expression ((ARRAY[...])::text[]). That escape hatch is unavailable on the right-hand side of LIKE, because LikeExpression's rhs production is SimpleExpression() and the ANY keyword is never reached as a function-name token in that slot.

SelectTest:3299 shows the same situation:

"SELECT * FROM pg_constraint WHERE pg_attribute.attnum = ANY(pg_constraint.conkey)"

Again, ANY(col) parses as a function call on the rhs of =.

A grep across src/ on master returns zero test cases for LIKE ANY, LIKE ALL, ILIKE ANY, or ILIKE ALL, so this combination genuinely is not covered.

Suggested direction (optional)

Two grammar changes appear to be needed together:

  1. In LikeExpression (around line 7059), allow the rhs to optionally dispatch to AnyComparisonExpression when the next token is ANY / SOME / ALL, instead of only SimpleExpression().
  2. In AnyComparisonExpression (around line 7402), accept an ArrayConstructor (or a parenthesized array / cast expression) in addition to ParenthesedSelect().

An alternative would be a dedicated AST node for LIKE ANY (ARRAY...) rather than overloading AnyComparisonExpression, but reusing AnyComparisonExpression looks more consistent with how = ANY (...) is already modeled.

Happy to put up a PR with the grammar change + tests for LIKE ANY (ARRAY[...]), LIKE ALL (ARRAY[...]), and the ILIKE variants once the maintainers confirm a preferred shape.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions