This is an automated email from the ASF dual-hosted git repository. liyang-kylin pushed a commit to branch kylin5 in repository https://gitbox.apache.org/repos/asf/kylin.git
commit 4a6dbe1716095263d515e86ddd4ff5207759abaa Author: Li Yang <[email protected]> AuthorDate: Sat Nov 7 16:13:07 2026 +0800 KYLIN-6126 bound SQL expression parser depth --- .../org/apache/kylin/query/util/EscapeParser.jj | 16 ++++++++++++++++ .../apache/kylin/query/util/WhiteSpaceParser.jj | 16 ++++++++++++++++ .../kylin/query/util/WhiteSpaceParserTest.java | 22 ++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/EscapeParser.jj b/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/EscapeParser.jj index 65899eaa5f..49043324df 100644 --- a/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/EscapeParser.jj +++ b/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/EscapeParser.jj @@ -32,8 +32,21 @@ public class EscapeParser { private static final String QUOTE_STR = "'"; private static final ImmutableSet<String> OPERATORS = ImmutableSet.of("+", "-", "*", "/", "%", "=", ">=", "<=", "!=", "<>", "||"); + private static final int MAX_EXPRESSION_NESTING_DEPTH = 256; private EscapeDialect dialect = EscapeDialect.DEFAULT; + private int expressionNestingDepth; + + private void enterExpression() throws ParseException { + if (++expressionNestingDepth > MAX_EXPRESSION_NESTING_DEPTH) { + expressionNestingDepth--; + throw new ParseException("SQL expression nesting exceeds " + MAX_EXPRESSION_NESTING_DEPTH); + } + } + + private void leaveExpression() { + expressionNestingDepth--; + } public static void main(String[] args) throws ParseException { System.out.println("Input SQL:"); @@ -192,6 +205,7 @@ TOKEN : /** Root production. */ String Input() : { + expressionNestingDepth = 0; String innerString; StringBuilder transformedStr = new StringBuilder(); } @@ -226,6 +240,7 @@ String Expression() : if (Thread.currentThread().isInterrupted()) { throw new ParseException("EscapeParser is interrupted"); } + enterExpression(); } ( innerString = EscapeExpress() @@ -255,6 +270,7 @@ String Expression() : | innerString = Any() ) { + leaveExpression(); return innerString; } } diff --git a/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/WhiteSpaceParser.jj b/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/WhiteSpaceParser.jj index c07936792a..179f43ea4c 100644 --- a/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/WhiteSpaceParser.jj +++ b/src/query-common/src/main/codegen/javacc/org/apache/kylin/query/util/WhiteSpaceParser.jj @@ -35,8 +35,21 @@ public class WhiteSpaceParser { private static final char SPACE_CHAR = ' '; private static final String QUOTE_STR = "'"; private static final Set<String> OPERATORS = new HashSet(Arrays.asList("+", "-", "*", "/", "%", "=",">=", "<=", "!=", "<>", "||")); + private static final int MAX_EXPRESSION_NESTING_DEPTH = 256; private EscapeDialect dialect = EscapeDialect.DEFAULT; + private int expressionNestingDepth; + + private void enterExpression() throws ParseException { + if (++expressionNestingDepth > MAX_EXPRESSION_NESTING_DEPTH) { + expressionNestingDepth--; + throw new ParseException("SQL expression nesting exceeds " + MAX_EXPRESSION_NESTING_DEPTH); + } + } + + private void leaveExpression() { + expressionNestingDepth--; + } public static void main(String[] args) throws Exception { String sql = "select * col1 ,\ncol2,col3 from \n tableA \\r a left \t\n table\rb ( select * from t) "; @@ -200,6 +213,7 @@ TOKEN : String parse(): { + expressionNestingDepth = 0; StringBuilder transformedStr = new StringBuilder(); String inner; } @@ -232,6 +246,7 @@ String Expression() : if (Thread.currentThread().isInterrupted()) { throw new ParseException("EscapeParser is interrupted"); } + enterExpression(); } (innerString = EscapeExpress() | innerString = SubstringExpression() @@ -260,6 +275,7 @@ String Expression() : | innerString = Any() ) { + leaveExpression(); return innerString; } } diff --git a/src/query/src/test/java/org/apache/kylin/query/util/WhiteSpaceParserTest.java b/src/query/src/test/java/org/apache/kylin/query/util/WhiteSpaceParserTest.java index 271828d66d..30f8af7b3c 100644 --- a/src/query/src/test/java/org/apache/kylin/query/util/WhiteSpaceParserTest.java +++ b/src/query/src/test/java/org/apache/kylin/query/util/WhiteSpaceParserTest.java @@ -18,6 +18,7 @@ package org.apache.kylin.query.util; +import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import org.junit.Test; @@ -92,6 +93,27 @@ public class WhiteSpaceParserTest { testBinary(sql3); } + @Test + public void testExcessiveExpressionNestingRejected() throws ParseException { + String nestedSql = nestedExpression(300); + + ParseException whitespaceException = Assert.assertThrows(ParseException.class, + () -> new WhiteSpaceParser(dialect, nestedSql).parse()); + Assert.assertTrue(whitespaceException.getMessage().contains("nesting exceeds")); + + ParseException escapeException = Assert.assertThrows(ParseException.class, + () -> new EscapeParser(dialect, nestedSql).Input()); + Assert.assertTrue(escapeException.getMessage().contains("nesting exceeds")); + + String shallowSql = nestedExpression(64); + Assert.assertNotNull(new WhiteSpaceParser(dialect, shallowSql).parse()); + Assert.assertNotNull(new EscapeParser(dialect, shallowSql).Input()); + } + + private String nestedExpression(int depth) { + return "select " + StringUtils.repeat("(", depth) + "1=1" + StringUtils.repeat(")", depth); + } + private void testBinary(String sql) throws ParseException { WhiteSpaceParser whiteSpaceParser = new WhiteSpaceParser(dialect, sql.trim()); String parsed = whiteSpaceParser.parse();
