This is an automated email from the ASF dual-hosted git repository.

morrysnow pushed a commit to branch branch-3.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.1 by this push:
     new 299aeec07d0 branch-3.1: [fix](parser) parse string wrong when set 
NO_BACKSLASH_ESCAPES sql mode #53449 (#53542)
299aeec07d0 is described below

commit 299aeec07d0d8d790c0cde64811f56c13f1e8165
Author: morrySnow <[email protected]>
AuthorDate: Fri Jul 18 19:10:02 2025 +0800

    branch-3.1: [fix](parser) parse string wrong when set NO_BACKSLASH_ESCAPES 
sql mode #53449 (#53542)
    
    cherry picked from #53449
---
 .../antlr4/org/apache/doris/nereids/DorisLexer.g4  |  9 ++--
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  4 ++
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  6 +++
 .../apache/doris/nereids/parser/NereidsParser.java |  4 +-
 .../doris/nereids/parser/NereidsParserTest.java    | 60 ++++++++++++++++++++++
 .../expression/SimplifyArithmeticRuleTest.java     |  4 +-
 .../rules/expression/SimplifyRangeTest.java        |  2 +-
 .../SimplifyArithmeticComparisonRuleTest.java      |  4 +-
 .../doris/nereids/rules/rewrite/OrToInTest.java    |  2 +-
 9 files changed, 84 insertions(+), 11 deletions(-)

diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
index 7419b51e717..a127880a5ff 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
@@ -24,6 +24,7 @@ lexer grammar DorisLexer;
    * When true, parser should throw ParseExcetion for unclosed bracketed 
comment.
    */
   public boolean has_unclosed_bracketed_comment = false;
+  public boolean isNoBackslashEscapes = false;
 
   /**
    * Verify whether current token is a valid decimal token (which contains 
dot).
@@ -609,10 +610,10 @@ ATSIGN: '@';
 DOUBLEATSIGN: '@@';
 
 STRING_LITERAL
-    : '\'' ('\\'. | '\'\'' | ~('\'' | '\\'))* '\''
-    | '"' ( '\\'. | '""' | ~('"'| '\\') )* '"'
-    | 'R\'' (~'\'')* '\''
-    | 'R"'(~'"')* '"'
+    : {!isNoBackslashEscapes}? '\'' ('\\'. | '\'\'' | ~('\'' | '\\'))* '\''
+    | {isNoBackslashEscapes}? '\'' ('\'\'' | ~('\''))* '\''
+    | {!isNoBackslashEscapes}?'"' ( '\\'. | '""' | ~('"'| '\\'))* '"'
+    | {isNoBackslashEscapes}?'"' ('""' | ~('"'))* '"'
     ;
 
 LEADING_STRING
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index def42b80df2..5d95771127d 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -33,6 +33,10 @@ singleStatement
     : SEMICOLON* statement? SEMICOLON* EOF
     ;
 
+expressionWithEof
+    : expression EOF
+    ;
+
 statement
     : statementBase # statementBaseAlias
     | CALL name=multipartIdentifier LEFT_PAREN (expression (COMMA 
expression)*)? RIGHT_PAREN #callProcedure
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 727d99d4890..9713077d019 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -103,6 +103,7 @@ import 
org.apache.doris.nereids.DorisParser.ElementAtContext;
 import org.apache.doris.nereids.DorisParser.ExistContext;
 import org.apache.doris.nereids.DorisParser.ExplainContext;
 import org.apache.doris.nereids.DorisParser.ExportContext;
+import org.apache.doris.nereids.DorisParser.ExpressionWithEofContext;
 import org.apache.doris.nereids.DorisParser.FixedPartitionDefContext;
 import org.apache.doris.nereids.DorisParser.FromClauseContext;
 import org.apache.doris.nereids.DorisParser.GroupingElementContext;
@@ -550,6 +551,11 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return ParserUtils.withOrigin(ctx, () -> (LogicalPlan) 
visit(ctx.statement()));
     }
 
+    @Override
+    public Expression visitExpressionWithEof(ExpressionWithEofContext ctx) {
+        return ParserUtils.withOrigin(ctx, () -> typedVisit(ctx.expression()));
+    }
+
     @Override
     public LogicalPlan visitStatementDefault(StatementDefaultContext ctx) {
         LogicalPlan plan = plan(ctx.query());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java
index c273f50b04a..f9ac5b724ef 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java
@@ -36,6 +36,7 @@ import org.apache.doris.plugin.DialectConverterPlugin;
 import org.apache.doris.plugin.PluginMgr;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.qe.SqlModeHelper;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -289,7 +290,7 @@ public class NereidsParser {
         if (isSimpleIdentifier(expression)) {
             return new UnboundSlot(expression);
         }
-        return parse(expression, DorisParser::expression);
+        return parse(expression, DorisParser::expressionWithEof);
     }
 
     private static boolean isSimpleIdentifier(String expression) {
@@ -443,6 +444,7 @@ public class NereidsParser {
 
     private static CommonTokenStream parseAllTokens(String sql) {
         DorisLexer lexer = new DorisLexer(new 
CaseInsensitiveStream(CharStreams.fromString(sql)));
+        lexer.isNoBackslashEscapes = SqlModeHelper.hasNoBackSlashEscapes();
         CommonTokenStream tokenStream = new CommonTokenStream(lexer);
         tokenStream.fill();
         return tokenStream;
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
index e7b3349c558..853414ac6f5 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
@@ -27,6 +27,7 @@ import org.apache.doris.nereids.exceptions.ParseException;
 import org.apache.doris.nereids.glue.LogicalPlanAdapter;
 import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 import org.apache.doris.nereids.trees.plans.DistributeType;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
@@ -44,10 +45,13 @@ import org.apache.doris.nereids.types.DateType;
 import org.apache.doris.nereids.types.DecimalV2Type;
 import org.apache.doris.nereids.types.DecimalV3Type;
 import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.SqlModeHelper;
 import org.apache.doris.qe.StmtExecutor;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
 
 import java.util.List;
 import java.util.Optional;
@@ -696,4 +700,60 @@ public class NereidsParserTest extends ParserTestBase {
             Assertions.fail(ex);
         }
     }
+
+    @Test
+    public void testNoBackSlashEscapes() {
+        testNoBackSlashEscapes("''", "", "");
+        testNoBackSlashEscapes("\"\"", "", "");
+
+        testNoBackSlashEscapes("''''", "'", "'");
+        testNoBackSlashEscapes("\"\"\"\"", "\"", "\"");
+
+        testNoBackSlashEscapes("\"\\\\n\"", "\\\\n", "\\n");
+        testNoBackSlashEscapes("\"\\t\"", "\\t", "\t");
+
+        testNoBackSlashEscapes("'\\'''", "\\'", null);
+        testNoBackSlashEscapes("'\\''", null, "'");
+        testNoBackSlashEscapes("'\\\\''", null, null);
+
+        testNoBackSlashEscapes("'\\\"\"'", "\\\"\"", "\"\"");
+        testNoBackSlashEscapes("'\\\"'", "\\\"", "\"");
+        testNoBackSlashEscapes("'\\\\\"'", "\\\\\"", "\\\"");
+
+        testNoBackSlashEscapes("\"\\''\"", "\\''", "''");
+        testNoBackSlashEscapes("\"\\'\"", "\\'", "'");
+        testNoBackSlashEscapes("\"\\\\'\"", "\\\\'", "\\'");
+
+        testNoBackSlashEscapes("\"\\\"\"\"", "\\\"", null);
+        testNoBackSlashEscapes("\"\\\"\"", null, "\"");
+        testNoBackSlashEscapes("\"\\\\\"\"", null, null);
+    }
+
+    private void testNoBackSlashEscapes(String sql, String onResult, String 
offResult) {
+        NereidsParser nereidsParser = new NereidsParser();
+
+        // test on
+        try (MockedStatic<SqlModeHelper> helperMockedStatic = 
Mockito.mockStatic(SqlModeHelper.class)) {
+            
helperMockedStatic.when(SqlModeHelper::hasNoBackSlashEscapes).thenReturn(true);
+            if (onResult == null) {
+                Assertions.assertThrowsExactly(ParseException.class, () -> 
nereidsParser.parseExpression(sql),
+                        "should failed when NO_BACKSLASH_ESCAPES = 1: " + sql);
+            } else {
+                Assertions.assertEquals(onResult,
+                        ((StringLikeLiteral) 
nereidsParser.parseExpression(sql)).getStringValue());
+            }
+        }
+
+        // test off
+        try (MockedStatic<SqlModeHelper> helperMockedStatic = 
Mockito.mockStatic(SqlModeHelper.class)) {
+            
helperMockedStatic.when(SqlModeHelper::hasNoBackSlashEscapes).thenReturn(false);
+            if (offResult == null) {
+                Assertions.assertThrowsExactly(ParseException.class, () -> 
nereidsParser.parseExpression(sql),
+                        "should failed when NO_BACKSLASH_ESCAPES = 0: " + sql);
+            } else {
+                Assertions.assertEquals(offResult,
+                        ((StringLikeLiteral) 
nereidsParser.parseExpression(sql)).getStringValue());
+            }
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java
index f23aefe5267..de91bb7df97 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java
@@ -49,8 +49,8 @@ class SimplifyArithmeticRuleTest extends 
ExpressionRewriteTestHelper {
         assertRewriteAfterTypeCoercion("IA * IB / 2 * 2", "cast((IA * IB) as 
DOUBLE) / 1.0");
         assertRewriteAfterTypeCoercion("IA * IB / (2 * 2)", "cast((IA * IB) as 
DOUBLE) / 4.0");
         assertRewriteAfterTypeCoercion("IA * IB / (2 * 2)", "cast((IA * IB) as 
DOUBLE) / 4.0");
-        assertRewriteAfterTypeCoercion("IA * (IB / 2) * 2)", "cast(IA as 
DOUBLE) * cast(IB as DOUBLE) / 1.0");
-        assertRewriteAfterTypeCoercion("IA * (IB / 2) * (IC + 1))", "cast(IA 
as DOUBLE) * cast(IB as DOUBLE) * cast((IC + 1) as DOUBLE) / 2.0");
+        assertRewriteAfterTypeCoercion("IA * (IB / 2) * 2", "cast(IA as 
DOUBLE) * cast(IB as DOUBLE) / 1.0");
+        assertRewriteAfterTypeCoercion("IA * (IB / 2) * (IC + 1)", "cast(IA as 
DOUBLE) * cast(IB as DOUBLE) * cast((IC + 1) as DOUBLE) / 2.0");
         assertRewriteAfterTypeCoercion("IA * IB / 2 / IC * 2 * ID / 4", 
"(((cast((IA * IB) as DOUBLE) / cast(IC as DOUBLE)) * cast(ID as DOUBLE)) / 
4.0)");
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java
index c94a1f34894..01b8c06ff08 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java
@@ -195,7 +195,7 @@ public class SimplifyRangeTest extends ExpressionRewrite {
         assertRewrite("(TA + TC > 3 and TA + TC < 1) and TB < 5", "(TA + TC) 
is null and null and TB < 5");
         assertRewrite("(TA + TC > 3 and TA + TC < 1) or TB < 5", "((TA + TC) 
is null and null) OR TB < 5");
 
-        assertRewrite("(TA + TC > 3 OR TA < 1) AND TB = 2) AND IA =1", "(TA + 
TC > 3 OR TA < 1) AND TB = 2) AND IA =1");
+        assertRewrite("(TA + TC > 3 OR TA < 1) AND TB = 2 AND IA =1", "(TA + 
TC > 3 OR TA < 1) AND TB = 2 AND IA =1");
 
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyArithmeticComparisonRuleTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyArithmeticComparisonRuleTest.java
index 32857d4f4ae..bc5c9ba94ca 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyArithmeticComparisonRuleTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/SimplifyArithmeticComparisonRuleTest.java
@@ -40,7 +40,7 @@ class SimplifyArithmeticComparisonRuleTest extends 
ExpressionRewriteTestHelper {
         assertRewriteAfterSimplify("TA - 2 > 1", "cast(TA as SMALLINT) > (1 + 
2)");
         assertRewriteAfterSimplify("1 + TA > 2", "cast(TA as SMALLINT) > (2 - 
1)");
         assertRewriteAfterSimplify("-1 + TA > 2", "cast(TA as SMALLINT) > (2 - 
(-1))");
-        assertRewriteAfterSimplify("1 - TA > 2", "cast(TA as SMALLINT) < (1 - 
2))");
+        assertRewriteAfterSimplify("1 - TA > 2", "cast(TA as SMALLINT) < (1 - 
2)");
         assertRewriteAfterSimplify("-1 - TA > 2", "cast(TA as SMALLINT) < 
((-1) - 2)");
         assertRewriteAfterSimplify("2 * TA > 1", "((2 * TA) > 1)");
         assertRewriteAfterSimplify("-2 * TA > 1", "((-2 * TA) > 1)");
@@ -72,7 +72,7 @@ class SimplifyArithmeticComparisonRuleTest extends 
ExpressionRewriteTestHelper {
         assertRewriteAfterSimplify("TA - 2 > 200", "cast(TA as INT) > (200 + 
2)");
         assertRewriteAfterSimplify("1 + TA > 200", "cast(TA as INT) > (200 - 
1)");
         assertRewriteAfterSimplify("-1 + TA > 200", "cast(TA as INT) > (200 - 
(-1))");
-        assertRewriteAfterSimplify("1 - TA > 200", "cast(TA as INT) < (1 - 
200))");
+        assertRewriteAfterSimplify("1 - TA > 200", "cast(TA as INT) < (1 - 
200)");
         assertRewriteAfterSimplify("-1 - TA > 200", "cast(TA as INT) < ((-1) - 
200)");
         assertRewriteAfterSimplify("2 * TA > 200", "((2 * TA) > 200)");
         assertRewriteAfterSimplify("-2 * TA > 200", "((-2 * TA) > 200)");
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/OrToInTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/OrToInTest.java
index 12f07dc77be..d9a84d94c37 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/OrToInTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/OrToInTest.java
@@ -167,7 +167,7 @@ class OrToInTest extends ExpressionRewriteTestHelper {
     @Test
     void test13() {
         // no rewrite, because of "a like 'xyz'"
-        String expr = "a like 'xyz% or a=1 or a=2': no extract";
+        String expr = "a like 'xyz% or a=1 or a=2'";
         Expression expression = PARSER.parseExpression(expr);
         Expression rewritten = 
OrToIn.EXTRACT_MODE_INSTANCE.rewriteTree(expression, context);
         Assertions.assertEquals("(a like 'xyz% or a=1 or a=2')",


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to