diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 50d98ed83..43579a6be 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -765,27 +765,12 @@ public class CCJSqlParser extends AbstractJSqlParser { if (getToken(1).kind != OPENING_BRACKET) return false; if (getToken(2).kind != OPENING_BRACKET) return false; // Skip any run of opening brackets: ( ( ( ... ( SELECT - int i = 3; - while (getToken(i).kind == OPENING_BRACKET) i++; - int k = getToken(i).kind; - if (k != K_SELECT && k != K_WITH && k != K_VALUES) return false; - // Scan forward counting brackets to find the matching ) for token(2) - // We are now inside all the opening brackets; depth = number of brackets skipped - 1 - // (token(1) is the outer one, token(2..i-1) are the inner ones we scanned) - int depth = i - 2; // brackets from position 2 to i-1 inclusive - i++; - while (i <= 500) { - Token t; try { t = getToken(i); } catch (TokenMgrException e) { return false; } - if (t == null || t.kind == 0) return false; - if (t.kind == OPENING_BRACKET) depth++; - else if (t.kind == CLOSING_BRACKET) { - if (--depth == 0) break; - } - i++; - } - if (i > 500) return false; - // Token at position i+1 follows the innermost closing ) - Token next; try { next = getToken(i + 1); } catch (TokenMgrException e) { return false; } + Token innerOpen = getToken(2); + Token t = innerOpen; + while (t.kind == OPENING_BRACKET) t = nextWithItemToken(t); + if (t.kind != K_SELECT && t.kind != K_WITH && t.kind != K_VALUES) return false; + // Follow the token chain directly; repeated getToken(i) calls rescan from the start each time + Token next = skipBalancedBracketGroup(innerOpen); if (next == null) return false; return next.kind == K_UNION || next.kind == K_INTERSECT || next.kind == K_EXCEPT || next.kind == K_MINUS; diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java index 5112b2521..a09ab4be3 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -91,6 +91,8 @@ @Execution(ExecutionMode.CONCURRENT) public class SelectTest { + private static final String LONG_CONDITION = "B = 1 AND ".repeat(150) + "B = 1"; + private final CCJSqlParserManager parserManager = new CCJSqlParserManager(); @Test @@ -5362,6 +5364,18 @@ public void testSetOperationWithParenthesisIssue1094() throws JSQLParserExceptio "SELECT * FROM ((SELECT A FROM tbl) UNION DISTINCT (SELECT B FROM tbl2)) AS union1"); } + @Test + public void testSetOperationWithParenthesisAndLongFirstBranch() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM ((SELECT A FROM tbl WHERE " + LONG_CONDITION + + ") UNION ALL (SELECT A FROM tbl2)) AS union1"); + } + + @Test + public void testSetOperationInExpressionWithLongFirstBranch() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT * FROM tbl WHERE A IN ((SELECT A FROM tbl2 WHERE " + + LONG_CONDITION + ") UNION ALL (SELECT A FROM tbl3))"); + } + @Test public void testSetOperationWithParenthesisIssue1094_2() throws JSQLParserException { String sqlStr =