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();

Reply via email to