This is an automated email from the ASF dual-hosted git repository. morrysnow pushed a commit to branch mysql_trim in repository https://gitbox.apache.org/repos/asf/doris.git
commit b57c527ba4e81fb66f71464c208dd744f91f2b61 Author: morrySnow <[email protected]> AuthorDate: Tue Jul 29 16:47:58 2025 +0800 [feature](function) support MySQL dialect of function TRIM MySQL doc: https://dev.mysql.com/doc/refman/8.4/en/string-functions.html#function_trim ```sql TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str), TRIM([remstr FROM] str) ``` --- .../antlr4/org/apache/doris/nereids/DorisLexer.g4 | 3 + .../antlr4/org/apache/doris/nereids/DorisParser.g4 | 2 + .../doris/nereids/parser/LogicalPlanBuilder.java | 16 +++++ .../doris/nereids/parser/NereidsParserTest.java | 77 ++++++++++++++++++++++ 4 files changed, 98 insertions(+) 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 f6dd3858509..4e0fa471caf 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 @@ -106,6 +106,7 @@ BITOR: 'BITOR'; BITXOR: 'BITXOR'; BLOB: 'BLOB'; BOOLEAN: 'BOOLEAN'; +BOTH: 'BOTH'; BRANCH: 'BRANCH'; BRIEF: 'BRIEF'; BROKER: 'BROKER'; @@ -316,6 +317,7 @@ LAST: 'LAST'; LATERAL: 'LATERAL'; LDAP: 'LDAP'; LDAP_ADMIN_PASSWORD: 'LDAP_ADMIN_PASSWORD'; +LEADING: 'LEADING'; LEFT: 'LEFT'; LESS: 'LESS'; LEVEL: 'LEVEL'; @@ -524,6 +526,7 @@ TINYINT: 'TINYINT'; TO: 'TO'; TOKENIZER: 'TOKENIZER'; TOKEN_FILTER: 'TOKEN_FILTER'; +TRAILING: 'TRAILING'; TRANSACTION: 'TRANSACTION'; TRASH: 'TRASH'; TREE: 'TREE'; 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 c3af5ac02b7..c111a86701d 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 @@ -1568,6 +1568,8 @@ primaryExpression (ORDER BY sortItem (COMMA sortItem)*)? (SEPARATOR sep=expression)? RIGHT_PAREN (OVER windowSpec)? #groupConcat + | TRIM LEFT_PAREN + ((BOTH | LEADING | TRAILING) expression? | expression) FROM expression RIGHT_PAREN #trim | functionCallExpression #functionCall | value=primaryExpression LEFT_BRACKET index=valueExpression RIGHT_BRACKET #elementAt | value=primaryExpression LEFT_BRACKET begin=valueExpression 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 57558335ae5..1e922fb6abf 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 @@ -441,6 +441,7 @@ import org.apache.doris.nereids.DorisParser.TableNameContext; import org.apache.doris.nereids.DorisParser.TableSnapshotContext; import org.apache.doris.nereids.DorisParser.TableValuedFunctionContext; import org.apache.doris.nereids.DorisParser.TabletListContext; +import org.apache.doris.nereids.DorisParser.TrimContext; import org.apache.doris.nereids.DorisParser.TypeConstructorContext; import org.apache.doris.nereids.DorisParser.UninstallPluginContext; import org.apache.doris.nereids.DorisParser.UnitIdentifierContext; @@ -3043,6 +3044,21 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> { }); } + @Override + public Object visitTrim(TrimContext ctx) { + return ParserUtils.withOrigin(ctx, () -> { + List<Expression> params = visit(ctx.expression(), Expression.class); + params = Lists.reverse(params); + String name = "trim"; + if (ctx.LEADING() != null) { + name = "ltrim"; + } else if (ctx.TRAILING() != null) { + name = "rtrim"; + } + return processUnboundFunction(ctx, null, name, false, params, null, null); + }); + } + @Override public Expression visitFunctionCallExpression(DorisParser.FunctionCallExpressionContext ctx) { return ParserUtils.withOrigin(ctx, () -> { 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 00292a01c99..b606483bf53 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 @@ -876,6 +876,83 @@ public class NereidsParserTest extends ParserTestBase { } } + @Test + public void testTrim() { + NereidsParser parser = new NereidsParser(); + String sql; + Expression e; + UnboundFunction unboundFunction; + + sql = "trim('1', '2')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("trim", unboundFunction.getName()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + Assertions.assertEquals("2", ((StringLikeLiteral) unboundFunction.child(1)).getStringValue()); + + sql = "trim('2' from '1')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("trim", unboundFunction.getName()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + Assertions.assertEquals("2", ((StringLikeLiteral) unboundFunction.child(1)).getStringValue()); + + sql = "trim(both '2' from '1')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("trim", unboundFunction.getName()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + Assertions.assertEquals("2", ((StringLikeLiteral) unboundFunction.child(1)).getStringValue()); + + sql = "trim(leading '2' from '1')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("ltrim", unboundFunction.getName()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + Assertions.assertEquals("2", ((StringLikeLiteral) unboundFunction.child(1)).getStringValue()); + + sql = "trim(trailing '2' from '1')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("rtrim", unboundFunction.getName()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + Assertions.assertEquals("2", ((StringLikeLiteral) unboundFunction.child(1)).getStringValue()); + + sql = "trim(both from '1')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("trim", unboundFunction.getName()); + Assertions.assertEquals(1, unboundFunction.arity()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + + sql = "trim(leading from '1')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("ltrim", unboundFunction.getName()); + Assertions.assertEquals(1, unboundFunction.arity()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + + sql = "trim(trailing from '1')"; + e = parser.parseExpression(sql); + Assertions.assertInstanceOf(UnboundFunction.class, e); + unboundFunction = (UnboundFunction) e; + Assertions.assertEquals("rtrim", unboundFunction.getName()); + Assertions.assertEquals(1, unboundFunction.arity()); + Assertions.assertEquals("1", ((StringLikeLiteral) unboundFunction.child(0)).getStringValue()); + + Assertions.assertThrowsExactly(ParseException.class, () -> parser.parseExpression("trim(invalid '2' from '1')")); + Assertions.assertThrowsExactly(ParseException.class, () -> parser.parseExpression("trim(invalid '2' '1')")); + Assertions.assertThrowsExactly(ParseException.class, () -> parser.parseExpression("trim(from '1')")); + Assertions.assertThrowsExactly(ParseException.class, () -> parser.parseExpression("trim(both '1')")); + } + @Test public void testNoBackSlashEscapes() { testNoBackSlashEscapes("''", "", ""); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
