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

zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new c7726171bc5 Support for the REGEXP function and the SELECT INTO 
OUTFILE statement and the CREATE FUNCTION statement (#37509)
c7726171bc5 is described below

commit c7726171bc515ec2eb1f1ec4762f745dbd58069f
Author: cxy <[email protected]>
AuthorDate: Fri Dec 26 11:59:09 2025 +0800

    Support for the REGEXP function and the SELECT INTO OUTFILE statement and 
the CREATE FUNCTION statement (#37509)
    
    * Support for the REGEXP function and the SELECT INTO OUTFILE statement and 
the CREATE FUNCTION statement
    
    * Support for the REGEXP function and the SELECT INTO OUTFILE statement and 
the CREATE FUNCTION statement
    
    * Support for the REGEXP function and the SELECT INTO OUTFILE statement and 
the CREATE FUNCTION statement
---
 .../src/main/antlr4/imports/doris/BaseRule.g4      |  10 +-
 .../src/main/antlr4/imports/doris/DDLStatement.g4  |  10 +-
 .../src/main/antlr4/imports/doris/DMLStatement.g4  |  14 +-
 .../src/main/antlr4/imports/doris/DorisKeyword.g4  |  12 ++
 .../visitor/statement/DorisStatementVisitor.java   |  98 +++++++++++++
 .../statement/type/DorisDDLStatementVisitor.java   |  79 ++++++++++-
 .../segment/dml/outfile/OutfileColumnsSegment.java |  71 ++++++++++
 .../segment/dml/outfile/OutfileLinesSegment.java   |  58 ++++++++
 .../core/segment/dml/outfile/OutfileSegment.java   |  94 +++++++++++++
 .../core/statement/type/dml/SelectStatement.java   |  12 ++
 .../doris/ddl/DorisCreateFunctionStatement.java    |  68 +++++++++
 .../segment/outfile/OutfileClauseAssert.java       |  81 +++++++++++
 .../doris/DorisCreateFunctionStatementAssert.java  | 152 +++++++++++++++++++++
 .../ddl/dialect/doris/DorisDDLStatementAssert.java |   4 +
 .../dml/standard/type/SelectStatementAssert.java   |  13 ++
 .../impl/outfile/ExpectedOutfileClause.java        |  59 ++++++++
 .../segment/impl/type/ExpectedDataTypeSegment.java |  35 +++++
 .../function/CreateFunctionStatementTestCase.java  |  31 +++++
 .../dml/standard/SelectStatementTestCase.java      |   4 +
 .../main/resources/case/ddl/create-function.xml    | 112 +++++++++++++++
 .../src/main/resources/case/dml/select-into.xml    |  61 +++++++++
 .../resources/case/dml/select-special-function.xml |  34 +++++
 .../sql/supported/ddl/create-function.xml          |   5 +
 .../resources/sql/supported/dml/select-into.xml    |   5 +
 .../sql/supported/dml/select-special-function.xml  |   2 +
 25 files changed, 1117 insertions(+), 7 deletions(-)

diff --git 
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4 
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4
index 85113a5d9ae..80438878762 100644
--- a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4
+++ b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4
@@ -137,6 +137,9 @@ identifierKeywordsUnambiguous
     | AGAINST
     | AGGREGATE
     | ALGORITHM
+    // DORIS ADDED BEGIN
+    | ALIAS
+    // DORIS ADDED END
     | ALWAYS
     | ANY
     | ARRAY
@@ -370,6 +373,9 @@ identifierKeywordsUnambiguous
     | OWNER
     | PACK_KEYS
     | PAGE
+    // DORIS ADDED BEGIN
+    | PARAMETER
+    // DORIS ADDED END
     | PARSER
     | PARTIAL
     | PARTITIONING
@@ -1235,7 +1241,7 @@ indexAlias
 // DORIS ADDED END
 
 regularFunctionName
-    : IF | LOCALTIME | LOCALTIMESTAMP | REPLACE | INSERT | INTERVAL | MOD
+    : IF | LOCALTIME | LOCALTIMESTAMP | REPLACE | REGEXP | INSERT | INTERVAL | 
MOD
     | DATABASE | SCHEMA | LEFT | RIGHT | DATE | DAY | GEOMETRYCOLLECTION | 
REPEAT
     | LINESTRING | MULTILINESTRING | MULTIPOINT | MULTIPOLYGON | POINT | 
POLYGON
     | TIME | TIMESTAMP | TIMESTAMP_ADD | TIMESTAMP_DIFF | DATE | 
CURRENT_TIMESTAMP 
@@ -1334,6 +1340,8 @@ dataType
     | dataTypeName = ENUM stringList charsetWithOptBinary?
     | dataTypeName = SET stringList charsetWithOptBinary?
     | dataTypeName = (SERIAL | JSON | GEOMETRY | GEOMCOLLECTION | 
GEOMETRYCOLLECTION | POINT | MULTIPOINT | LINESTRING | MULTILINESTRING | 
POLYGON | MULTIPOLYGON)
+    | dataTypeName = STRING
+    | dataTypeName = ARRAY
     ;
 
 stringList
diff --git 
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DDLStatement.g4 
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DDLStatement.g4
index fadc6185096..c1fbec21d4d 100644
--- 
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DDLStatement.g4
+++ 
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DDLStatement.g4
@@ -60,7 +60,7 @@ properties
     ;
 
 property
-    : (identifier | SINGLE_QUOTED_TEXT) EQ_? literals
+    : (identifier | SINGLE_QUOTED_TEXT | DOUBLE_QUOTED_TEXT) EQ_? literals
     ;
 // DORIS ADDED END
 
@@ -309,7 +309,13 @@ dropEvent
     ;
 
 createFunction
-    : CREATE ownerStatement?
+    : CREATE GLOBAL? (AGGREGATE | TABLES | ALIAS)? FUNCTION functionName
+      LP_ (dataType (COMMA_ dataType)*)? RP_
+      (RETURNS dataType)?
+      (INTERMEDIATE dataType)?
+      (WITH PARAMETER LP_ identifier (COMMA_ identifier)* RP_ AS expr)?
+      (PROPERTIES LP_ properties RP_)?
+    | CREATE ownerStatement?
       FUNCTION functionName LP_ (identifier dataType)? (COMMA_ identifier 
dataType)* RP_
       RETURNS dataType
       routineOption*
diff --git 
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DMLStatement.g4 
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DMLStatement.g4
index a94fb3c2850..cd41583d94c 100644
--- 
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DMLStatement.g4
+++ 
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DMLStatement.g4
@@ -383,7 +383,19 @@ selectFieldsInto
 
 selectIntoExpression
     : INTO variable (COMMA_ variable )* | INTO DUMPFILE string_
-    | (INTO OUTFILE string_ (CHARACTER SET charsetName)?(COLUMNS 
selectFieldsInto+)? (LINES selectLinesInto+)?)
+    | INTO OUTFILE string_ (CHARACTER SET charsetName)?(COLUMNS 
selectFieldsInto+)? (LINES selectLinesInto+)? (FORMAT AS formatName)? 
outfileProperties?
+    ;
+
+formatName
+    : identifier
+    ;
+
+outfileProperties
+    : PROPERTIES LP_ outfileProperty (COMMA_ outfileProperty)* RP_
+    ;
+
+outfileProperty
+    : string_ EQ_ string_
     ;
 
 lockClause
diff --git 
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DorisKeyword.g4 
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DorisKeyword.g4
index 500f9648758..e84be24132e 100644
--- 
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DorisKeyword.g4
+++ 
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/DorisKeyword.g4
@@ -131,6 +131,10 @@ ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
     : A S S I G N UL_ G T I D S UL_ T O UL_ A N O N Y M O U S UL_ T R A N S A 
C T I O N S
     ;
 
+ALIAS
+    : A L I A S
+    ;
+
 // DORIS ADDED BEGIN
 BITXOR
     : B I T X O R
@@ -1204,6 +1208,10 @@ ITERATE
     : I T E R A T E
     ;
 
+INTERMEDIATE
+    : I N T E R M E D I A T E
+    ;
+
 JOIN
     : J O I N
     ;
@@ -2004,6 +2012,10 @@ PURGE
     : P U R G E
     ;
 
+PARAMETER
+    : P A R A M E T E R
+    ;
+
 QUARTER
     : Q U A R T E R
     ;
diff --git 
a/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/DorisStatementVisitor.java
 
b/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/DorisStatementVisitor.java
index e4a4f98fa35..b64ba91cec6 100644
--- 
a/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/DorisStatementVisitor.java
+++ 
b/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/DorisStatementVisitor.java
@@ -99,6 +99,10 @@ import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.OnDupli
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.OrderByClauseContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.OrderByItemContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.OwnerContext;
+import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.OutfilePropertyContext;
+import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.SelectFieldsIntoContext;
+import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.SelectIntoExpressionContext;
+import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.SelectLinesIntoContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ParameterMarkerContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.PositionFunctionContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.PrecisionContext;
@@ -152,6 +156,9 @@ import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ViewNam
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.WeightStringFunctionContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.WhereClauseContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.WithClauseContext;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile.OutfileColumnsSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile.OutfileLinesSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile.OutfileSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.enums.AggregationType;
 import org.apache.shardingsphere.sql.parser.statement.core.enums.CombineType;
 import org.apache.shardingsphere.sql.parser.statement.core.enums.JoinType;
@@ -250,6 +257,8 @@ import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.util.Map;
+import java.util.LinkedHashMap;
 
 /**
  * Statement visitor for Doris.
@@ -738,12 +747,93 @@ public abstract class DorisStatementVisitor extends 
DorisStatementBaseVisitor<AS
             return visit(ctx.selectWithInto());
         }
         SelectStatement result = (SelectStatement) 
visit(ctx.queryExpression());
+        if (null != ctx.selectIntoExpression()) {
+            ASTNode intoSegment = visit(ctx.selectIntoExpression());
+            if (intoSegment instanceof OutfileSegment) {
+                result.setOutfile((OutfileSegment) intoSegment);
+            } else if (intoSegment instanceof TableSegment) {
+                result.setInto((TableSegment) intoSegment);
+            }
+        }
         if (null != ctx.lockClauseList()) {
             result.setLock((LockSegment) visit(ctx.lockClauseList()));
         }
         return result;
     }
     
+    @Override
+    public ASTNode visitSelectIntoExpression(final SelectIntoExpressionContext 
ctx) {
+        if (null != ctx.OUTFILE()) {
+            String filePath = ctx.string_().getText();
+            filePath = SQLUtils.getExactlyValue(filePath);
+            String characterSet = null;
+            if (null != ctx.charsetName()) {
+                characterSet = ctx.charsetName().getText();
+            }
+            OutfileColumnsSegment columns = null;
+            if (null != ctx.selectFieldsInto() && 
!ctx.selectFieldsInto().isEmpty()) {
+                columns = createOutfileColumnsSegment(ctx);
+            }
+            OutfileLinesSegment lines = null;
+            if (null != ctx.selectLinesInto() && 
!ctx.selectLinesInto().isEmpty()) {
+                lines = createOutfileLinesSegment(ctx);
+            }
+            String format = null;
+            if (null != ctx.formatName()) {
+                format = ctx.formatName().identifier().getText();
+            }
+            Map<String, String> properties = null;
+            if (null != ctx.outfileProperties()) {
+                properties = new LinkedHashMap<>();
+                for (OutfilePropertyContext each : 
ctx.outfileProperties().outfileProperty()) {
+                    String key = 
SQLUtils.getExactlyValue(each.string_(0).getText());
+                    String value = 
SQLUtils.getExactlyValue(each.string_(1).getText());
+                    properties.put(key, value);
+                }
+            }
+            return new OutfileSegment(ctx.getStart().getStartIndex(), 
ctx.getStop().getStopIndex(), filePath, format, properties, characterSet, 
columns, lines);
+        }
+        return visitChildren(ctx);
+    }
+    
+    private OutfileColumnsSegment createOutfileColumnsSegment(final 
SelectIntoExpressionContext ctx) {
+        int startIndex = ctx.selectFieldsInto(0).getStart().getStartIndex();
+        int stopIndex = ctx.selectFieldsInto(ctx.selectFieldsInto().size() - 
1).getStop().getStopIndex();
+        String terminatedBy = null;
+        String enclosedBy = null;
+        String escapedBy = null;
+        boolean optionallyEnclosed = false;
+        for (SelectFieldsIntoContext each : ctx.selectFieldsInto()) {
+            if (null != each.TERMINATED()) {
+                terminatedBy = 
SQLUtils.getExactlyValue(each.string_().getText());
+            }
+            if (null != each.ENCLOSED()) {
+                enclosedBy = 
SQLUtils.getExactlyValue(each.string_().getText());
+                optionallyEnclosed = null != each.OPTIONALLY();
+            }
+            if (null != each.ESCAPED()) {
+                escapedBy = SQLUtils.getExactlyValue(each.string_().getText());
+            }
+        }
+        return new OutfileColumnsSegment(startIndex, stopIndex, terminatedBy, 
enclosedBy, escapedBy, optionallyEnclosed);
+    }
+    
+    private OutfileLinesSegment createOutfileLinesSegment(final 
SelectIntoExpressionContext ctx) {
+        int startIndex = ctx.selectLinesInto(0).getStart().getStartIndex();
+        int stopIndex = ctx.selectLinesInto(ctx.selectLinesInto().size() - 
1).getStop().getStopIndex();
+        String startingBy = null;
+        String terminatedBy = null;
+        for (SelectLinesIntoContext each : ctx.selectLinesInto()) {
+            if (null != each.STARTING()) {
+                startingBy = 
SQLUtils.getExactlyValue(each.string_().getText());
+            }
+            if (null != each.TERMINATED()) {
+                terminatedBy = 
SQLUtils.getExactlyValue(each.string_().getText());
+            }
+        }
+        return new OutfileLinesSegment(startIndex, stopIndex, startingBy, 
terminatedBy);
+    }
+    
     @Override
     public ASTNode visitWithClause(final WithClauseContext ctx) {
         Collection<CommonTableExpressionSegment> commonTableExpressions = new 
LinkedList<>();
@@ -826,6 +916,14 @@ public abstract class DorisStatementVisitor extends 
DorisStatementBaseVisitor<AS
         if (null != ctx.windowClause()) {
             result.setWindow((WindowSegment) visit(ctx.windowClause()));
         }
+        if (null != ctx.selectIntoExpression()) {
+            ASTNode intoSegment = visit(ctx.selectIntoExpression());
+            if (intoSegment instanceof OutfileSegment) {
+                result.setOutfile((OutfileSegment) intoSegment);
+            } else if (intoSegment instanceof TableSegment) {
+                result.setInto((TableSegment) intoSegment);
+            }
+        }
         return result;
     }
     
diff --git 
a/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDDLStatementVisitor.java
 
b/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDDLStatementVisitor.java
index dade6bd0a3e..15d751510ba 100644
--- 
a/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDDLStatementVisitor.java
+++ 
b/parser/sql/engine/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/engine/doris/visitor/statement/type/DorisDDLStatementVisitor.java
@@ -98,6 +98,7 @@ import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.LoopSta
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ModifyColumnContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.PlaceContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.PrepareContext;
+import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.PropertyContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.ReferenceDefinitionContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.RenameColumnContext;
 import 
org.apache.shardingsphere.sql.parser.autogen.DorisStatementParser.RenameIndexContext;
@@ -146,6 +147,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.table.Loc
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.table.RenameTableDefinitionSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.tablespace.TablespaceSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.SimpleExpressionSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.CommentSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.DataTypeSegment;
@@ -161,7 +163,6 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.da
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.database.CreateDatabaseStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.database.DropDatabaseStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.function.AlterFunctionStatement;
-import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.function.CreateFunctionStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.function.DropFunctionStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.index.CreateIndexStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.index.DropIndexStatement;
@@ -192,6 +193,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.Up
 import org.apache.shardingsphere.sql.parser.statement.core.util.SQLUtils;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.collection.CollectionValue;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+import 
org.apache.shardingsphere.sql.parser.statement.doris.ddl.DorisCreateFunctionStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.doris.ddl.DorisResumeJobStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.mysql.ddl.event.MySQLAlterEventStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.mysql.ddl.event.MySQLCreateEventStatement;
@@ -796,12 +798,83 @@ public final class DorisDDLStatementVisitor extends 
DorisStatementVisitor implem
     
     @Override
     public ASTNode visitCreateFunction(final CreateFunctionContext ctx) {
-        CreateFunctionStatement result = new 
CreateFunctionStatement(getDatabaseType());
+        DorisCreateFunctionStatement result = new 
DorisCreateFunctionStatement(getDatabaseType());
         result.setFunctionName((FunctionNameSegment) 
visit(ctx.functionName()));
-        result.setRoutineBody((RoutineBodySegment) visit(ctx.routineBody()));
+        if (null != ctx.routineBody()) {
+            int paramIndex = 0;
+            for (int i = 0; i < ctx.dataType().size(); i++) {
+                DataTypeSegment dataType = (DataTypeSegment) 
visit(ctx.dataType(i));
+                if (i == ctx.dataType().size() - 1 && null != ctx.RETURNS()) {
+                    result.setReturnType(dataType);
+                } else if (paramIndex < ctx.identifier().size()) {
+                    IdentifierValue paramName = (IdentifierValue) 
visit(ctx.identifier(paramIndex));
+                    result.getNamedParameters().put(paramName, dataType);
+                    paramIndex++;
+                }
+            }
+            result.setRoutineBody((RoutineBodySegment) 
visit(ctx.routineBody()));
+        } else {
+            if (null != ctx.GLOBAL()) {
+                result.setGlobal(true);
+            }
+            if (null != ctx.AGGREGATE()) {
+                
result.setFunctionType(DorisCreateFunctionStatement.FunctionType.AGGREGATE);
+            } else if (null != ctx.TABLES()) {
+                
result.setFunctionType(DorisCreateFunctionStatement.FunctionType.TABLES);
+            } else if (null != ctx.ALIAS()) {
+                
result.setFunctionType(DorisCreateFunctionStatement.FunctionType.ALIAS);
+            }
+            int dataTypeCount = ctx.dataType().size();
+            int parameterCount = dataTypeCount;
+            if (null != ctx.INTERMEDIATE()) {
+                parameterCount--;
+            }
+            if (null != ctx.RETURNS()) {
+                parameterCount--;
+            }
+            for (int i = 0; i < parameterCount; i++) {
+                result.getParameterDataTypes().add((DataTypeSegment) 
visit(ctx.dataType(i)));
+            }
+            if (null != ctx.RETURNS()) {
+                result.setReturnType((DataTypeSegment) 
visit(ctx.dataType(parameterCount)));
+            }
+            if (null != ctx.INTERMEDIATE()) {
+                int intermediateTypeIndex = dataTypeCount - 1;
+                result.setIntermediateType((DataTypeSegment) 
visit(ctx.dataType(intermediateTypeIndex)));
+            }
+            if (null != ctx.PARAMETER()) {
+                for (int i = 0; i < ctx.identifier().size(); i++) {
+                    result.getWithParameters().add((IdentifierValue) 
visit(ctx.identifier(i)));
+                }
+            }
+            if (null != ctx.expr()) {
+                result.setAliasExpression((ExpressionSegment) 
visit(ctx.expr()));
+            }
+            if (null != ctx.PROPERTIES()) {
+                fillCreateFunctionProperties(ctx, result);
+            }
+        }
         return result;
     }
     
+    private void fillCreateFunctionProperties(final CreateFunctionContext ctx, 
final DorisCreateFunctionStatement statement) {
+        for (int i = 0; i < ctx.properties().property().size(); i++) {
+            String key = getPropertyKey(ctx.properties().property(i));
+            String value = 
SQLUtils.getExactlyValue(ctx.properties().property(i).literals().getText());
+            statement.getProperties().put(key, value);
+        }
+    }
+    
+    private String getPropertyKey(final PropertyContext property) {
+        if (null != property.SINGLE_QUOTED_TEXT()) {
+            return 
SQLUtils.getExactlyValue(property.SINGLE_QUOTED_TEXT().getText());
+        }
+        if (null != property.DOUBLE_QUOTED_TEXT()) {
+            return 
SQLUtils.getExactlyValue(property.DOUBLE_QUOTED_TEXT().getText());
+        }
+        return SQLUtils.getExactlyValue(property.identifier().getText());
+    }
+    
     @SuppressWarnings("unchecked")
     @Override
     public ASTNode visitRoutineBody(final RoutineBodyContext ctx) {
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileColumnsSegment.java
 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileColumnsSegment.java
new file mode 100644
index 00000000000..5ab301aeda3
--- /dev/null
+++ 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileColumnsSegment.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.SQLSegment;
+
+import java.util.Optional;
+
+/**
+ * Outfile columns segment.
+ */
+@RequiredArgsConstructor
+@Getter
+public final class OutfileColumnsSegment implements SQLSegment {
+    
+    private final int startIndex;
+    
+    private final int stopIndex;
+    
+    private final String terminatedBy;
+    
+    private final String enclosedBy;
+    
+    private final String escapedBy;
+    
+    private final boolean optionallyEnclosed;
+    
+    /**
+     * Get terminated by.
+     *
+     * @return terminated by
+     */
+    public Optional<String> getTerminatedBy() {
+        return Optional.ofNullable(terminatedBy);
+    }
+    
+    /**
+     * Get enclosed by.
+     *
+     * @return enclosed by
+     */
+    public Optional<String> getEnclosedBy() {
+        return Optional.ofNullable(enclosedBy);
+    }
+    
+    /**
+     * Get escaped by.
+     *
+     * @return escaped by
+     */
+    public Optional<String> getEscapedBy() {
+        return Optional.ofNullable(escapedBy);
+    }
+}
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileLinesSegment.java
 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileLinesSegment.java
new file mode 100644
index 00000000000..af2ee09992d
--- /dev/null
+++ 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileLinesSegment.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.SQLSegment;
+
+import java.util.Optional;
+
+/**
+ * Outfile lines segment.
+ */
+@RequiredArgsConstructor
+@Getter
+public final class OutfileLinesSegment implements SQLSegment {
+    
+    private final int startIndex;
+    
+    private final int stopIndex;
+    
+    private final String startingBy;
+    
+    private final String terminatedBy;
+    
+    /**
+     * Get starting by.
+     *
+     * @return starting by
+     */
+    public Optional<String> getStartingBy() {
+        return Optional.ofNullable(startingBy);
+    }
+    
+    /**
+     * Get terminated by.
+     *
+     * @return terminated by
+     */
+    public Optional<String> getTerminatedBy() {
+        return Optional.ofNullable(terminatedBy);
+    }
+}
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileSegment.java
 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileSegment.java
new file mode 100644
index 00000000000..8d7678d4cb4
--- /dev/null
+++ 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/outfile/OutfileSegment.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.SQLSegment;
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Outfile segment.
+ */
+@RequiredArgsConstructor
+@Getter
+public final class OutfileSegment implements SQLSegment {
+    
+    private final int startIndex;
+    
+    private final int stopIndex;
+    
+    private final String filePath;
+    
+    private final String format;
+    
+    private final Map<String, String> properties;
+    
+    private final String characterSet;
+    
+    private final OutfileColumnsSegment columns;
+    
+    private final OutfileLinesSegment lines;
+    
+    /**
+     * Get character set.
+     *
+     * @return character set
+     */
+    public Optional<String> getCharacterSet() {
+        return Optional.ofNullable(characterSet);
+    }
+    
+    /**
+     * Get columns.
+     *
+     * @return columns
+     */
+    public Optional<OutfileColumnsSegment> getColumns() {
+        return Optional.ofNullable(columns);
+    }
+    
+    /**
+     * Get lines.
+     *
+     * @return lines
+     */
+    public Optional<OutfileLinesSegment> getLines() {
+        return Optional.ofNullable(lines);
+    }
+    
+    /**
+     * Get format.
+     *
+     * @return format
+     */
+    public Optional<String> getFormat() {
+        return Optional.ofNullable(format);
+    }
+    
+    /**
+     * Get properties.
+     *
+     * @return properties
+     */
+    public Optional<Map<String, String>> getProperties() {
+        return Optional.ofNullable(properties);
+    }
+}
diff --git 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/dml/SelectStatement.java
 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/dml/SelectStatement.java
index 8a1125a1c12..77ba22aa3c0 100644
--- 
a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/dml/SelectStatement.java
+++ 
b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/statement/type/dml/SelectStatement.java
@@ -34,6 +34,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.Model
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.WindowSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.WithSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile.OutfileSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.SQLStatementAttributes;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.AllowNotUseDatabaseSQLStatementAttribute;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.WithSQLStatementAttribute;
@@ -75,6 +76,8 @@ public final class SelectStatement extends DMLStatement {
     
     private ModelSegment model;
     
+    private OutfileSegment outfile;
+    
     private WithTableHintSegment withTableHint;
     
     public SelectStatement(final DatabaseType databaseType) {
@@ -198,6 +201,15 @@ public final class SelectStatement extends DMLStatement {
         return Optional.ofNullable(model);
     }
     
+    /**
+     * Get outfile.
+     *
+     * @return outfile segment
+     */
+    public Optional<OutfileSegment> getOutfile() {
+        return Optional.ofNullable(outfile);
+    }
+    
     /**
      * Get with table hint.
      *
diff --git 
a/parser/sql/statement/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/statement/doris/ddl/DorisCreateFunctionStatement.java
 
b/parser/sql/statement/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/statement/doris/ddl/DorisCreateFunctionStatement.java
new file mode 100644
index 00000000000..966e548be1f
--- /dev/null
+++ 
b/parser/sql/statement/dialect/doris/src/main/java/org/apache/shardingsphere/sql/parser/statement/doris/ddl/DorisCreateFunctionStatement.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.sql.parser.statement.doris.ddl;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.DataTypeSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.function.CreateFunctionStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Create function statement for Doris.
+ */
+@Getter
+@Setter
+public final class DorisCreateFunctionStatement extends 
CreateFunctionStatement {
+    
+    private boolean global;
+    
+    private FunctionType functionType;
+    
+    private final List<DataTypeSegment> parameterDataTypes = new 
LinkedList<>();
+    
+    private final Map<IdentifierValue, DataTypeSegment> namedParameters = new 
LinkedHashMap<>();
+    
+    private DataTypeSegment returnType;
+    
+    private DataTypeSegment intermediateType;
+    
+    private final List<IdentifierValue> withParameters = new LinkedList<>();
+    
+    private ExpressionSegment aliasExpression;
+    
+    private final Map<String, String> properties = new LinkedHashMap<>();
+    
+    public DorisCreateFunctionStatement(final DatabaseType databaseType) {
+        super(databaseType);
+    }
+    
+    /**
+     * Function type enumeration for Doris.
+     */
+    public enum FunctionType {
+        AGGREGATE, TABLES, ALIAS
+    }
+}
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/outfile/OutfileClauseAssert.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/outfile/OutfileClauseAssert.java
new file mode 100644
index 00000000000..e29d3ced607
--- /dev/null
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/outfile/OutfileClauseAssert.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.outfile;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile.OutfileSegment;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.SQLSegmentAssert;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.outfile.ExpectedOutfileClause;
+
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Outfile clause assert.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class OutfileClauseAssert {
+    
+    /**
+     * Assert outfile segment is correct with expected outfile clause.
+     *
+     * @param assertContext assert context
+     * @param actual actual outfile segment
+     * @param expected expected outfile clause
+     */
+    public static void assertIs(final SQLCaseAssertContext assertContext, 
final OutfileSegment actual, final ExpectedOutfileClause expected) {
+        assertFilePath(assertContext, actual.getFilePath(), 
expected.getFilePath());
+        assertFormat(assertContext, actual.getFormat().orElse(null), 
expected.getFormat());
+        assertProperties(assertContext, actual.getProperties().orElse(null), 
expected.getProperties());
+        SQLSegmentAssert.assertIs(assertContext, actual, expected);
+    }
+    
+    private static void assertFilePath(final SQLCaseAssertContext 
assertContext, final String actual, final String expected) {
+        assertThat(assertContext.getText("Outfile path assertion error: "), 
actual, is(expected));
+    }
+    
+    private static void assertFormat(final SQLCaseAssertContext assertContext, 
final String actual, final String expected) {
+        if (null == expected) {
+            assertNull(actual, assertContext.getText("Actual outfile format 
should not exist."));
+        } else {
+            assertNotNull(actual, assertContext.getText("Actual outfile format 
should exist."));
+            assertThat(assertContext.getText("Outfile format assertion error: 
"), actual, is(expected));
+        }
+    }
+    
+    private static void assertProperties(final SQLCaseAssertContext 
assertContext, final Map<String, String> actual, final Map<String, String> 
expected) {
+        if (null == expected || expected.isEmpty()) {
+            if (null != actual && !actual.isEmpty()) {
+                assertThat(assertContext.getText("Actual outfile properties 
should be empty."), actual.isEmpty(), is(true));
+            }
+        } else {
+            assertNotNull(actual, assertContext.getText("Actual outfile 
properties should exist."));
+            assertThat(assertContext.getText("Outfile properties size 
assertion error: "), actual.size(), is(expected.size()));
+            for (Map.Entry<String, String> entry : expected.entrySet()) {
+                assertThat(assertContext.getText(String.format("Outfile 
property '%s' assertion error: ", entry.getKey())),
+                        actual.get(entry.getKey()), is(entry.getValue()));
+            }
+        }
+    }
+}
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/dialect/doris/DorisCreateFunctionStatementAssert.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/dialect/doris/DorisCreateFunctionStatementAssert.java
new file mode 100644
index 00000000000..a971fee3a8c
--- /dev/null
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/dialect/doris/DorisCreateFunctionStatementAssert.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.statement.ddl.dialect.doris;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.DataTypeSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+import 
org.apache.shardingsphere.sql.parser.statement.doris.ddl.DorisCreateFunctionStatement;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.SQLSegmentAssert;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.expression.ExpressionAssert;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.distsql.ExpectedProperty;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.type.ExpectedDataTypeSegment;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.statement.ddl.standard.function.CreateFunctionStatementTestCase;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Create function statement assert for Doris.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class DorisCreateFunctionStatementAssert {
+    
+    /**
+     * Assert create function statement is correct with expected parser result.
+     *
+     * @param assertContext assert context
+     * @param actual actual create function statement
+     * @param expected expected create function statement test case
+     */
+    public static void assertIs(final SQLCaseAssertContext assertContext, 
final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        assertFunctionName(assertContext, actual, expected);
+        assertGlobal(assertContext, actual, expected);
+        assertFunctionType(assertContext, actual, expected);
+        assertParameterDataTypes(assertContext, actual, expected);
+        assertReturnType(assertContext, actual, expected);
+        assertIntermediateType(assertContext, actual, expected);
+        assertWithParameters(assertContext, actual, expected);
+        assertAliasExpression(assertContext, actual, expected);
+        assertProperties(assertContext, actual, expected);
+    }
+    
+    private static void assertFunctionName(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (null != expected.getFunctionName()) {
+            assertTrue(actual.getFunctionName().isPresent(), 
assertContext.getText("Function name should not be null"));
+            assertThat(assertContext.getText("Function name assertion error: 
"),
+                    actual.getFunctionName().get().getIdentifier().getValue(), 
is(expected.getFunctionName().getName()));
+            SQLSegmentAssert.assertIs(assertContext, 
actual.getFunctionName().get(), expected.getFunctionName());
+        }
+    }
+    
+    private static void assertGlobal(final SQLCaseAssertContext assertContext, 
final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (null != expected.getGlobal()) {
+            assertThat(assertContext.getText("Global flag assertion error: "), 
actual.isGlobal(), is(expected.getGlobal()));
+        }
+    }
+    
+    private static void assertFunctionType(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (null != expected.getFunctionType()) {
+            assertNotNull(actual.getFunctionType(), 
assertContext.getText("Function type should not be null"));
+            assertThat(assertContext.getText("Function type assertion error: 
"),
+                    actual.getFunctionType().name(), 
is(expected.getFunctionType()));
+        }
+    }
+    
+    private static void assertParameterDataTypes(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual,
+                                                 final 
CreateFunctionStatementTestCase expected) {
+        if (!expected.getParameterDataTypes().isEmpty()) {
+            List<DataTypeSegment> actualParams = 
actual.getParameterDataTypes();
+            List<ExpectedDataTypeSegment> expectedParams = 
expected.getParameterDataTypes();
+            assertThat(assertContext.getText("Parameter count assertion error: 
"),
+                    actualParams.size(), is(expectedParams.size()));
+            for (int i = 0; i < actualParams.size(); i++) {
+                assertThat(assertContext.getText(String.format("Parameter %d 
type assertion error: ", i)),
+                        actualParams.get(i).getDataTypeName(), 
is(expectedParams.get(i).getName()));
+            }
+        }
+    }
+    
+    private static void assertReturnType(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (null != expected.getReturnType()) {
+            assertNotNull(actual.getReturnType(), 
assertContext.getText("Return type should not be null"));
+            assertThat(assertContext.getText("Return type assertion error: "),
+                    actual.getReturnType().getDataTypeName(), 
is(expected.getReturnType().getName()));
+        }
+    }
+    
+    private static void assertIntermediateType(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (null != expected.getIntermediateType()) {
+            assertNotNull(actual.getIntermediateType(), 
assertContext.getText("Intermediate type should not be null"));
+            assertThat(assertContext.getText("Intermediate type assertion 
error: "),
+                    actual.getIntermediateType().getDataTypeName(), 
is(expected.getIntermediateType().getName()));
+        }
+    }
+    
+    private static void assertWithParameters(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (!expected.getWithParameters().isEmpty()) {
+            List<IdentifierValue> actualWithParams = 
actual.getWithParameters();
+            List<String> expectedWithParams = expected.getWithParameters();
+            assertThat(assertContext.getText("WITH PARAMETER count assertion 
error: "),
+                    actualWithParams.size(), is(expectedWithParams.size()));
+            for (int i = 0; i < actualWithParams.size(); i++) {
+                assertThat(assertContext.getText(String.format("WITH PARAMETER 
%d assertion error: ", i)),
+                        actualWithParams.get(i).getValue(), 
is(expectedWithParams.get(i)));
+            }
+        }
+    }
+    
+    private static void assertAliasExpression(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (null != expected.getAliasExpression()) {
+            assertNotNull(actual.getAliasExpression(), 
assertContext.getText("Alias expression should not be null"));
+            ExpressionAssert.assertExpression(assertContext, 
actual.getAliasExpression(), expected.getAliasExpression());
+        }
+    }
+    
+    private static void assertProperties(final SQLCaseAssertContext 
assertContext, final DorisCreateFunctionStatement actual, final 
CreateFunctionStatementTestCase expected) {
+        if (!expected.getProperties().isEmpty()) {
+            Map<String, String> actualProps = actual.getProperties();
+            List<ExpectedProperty> expectedProps = expected.getProperties();
+            assertFalse(actualProps.isEmpty(), 
assertContext.getText("Properties should not be empty"));
+            for (ExpectedProperty expectedProp : expectedProps) {
+                assertTrue(actualProps.containsKey(expectedProp.getKey()),
+                        assertContext.getText(String.format("Property key '%s' 
not found", expectedProp.getKey())));
+                assertThat(assertContext.getText(String.format("Property '%s' 
value assertion error: ", expectedProp.getKey())),
+                        actualProps.get(expectedProp.getKey()), 
is(expectedProp.getValue()));
+            }
+        }
+    }
+}
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/dialect/doris/DorisDDLStatementAssert.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/dialect/doris/DorisDDLStatementAssert.java
index 019cd340f4a..7a55da69cc5 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/dialect/doris/DorisDDLStatementAssert.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/ddl/dialect/doris/DorisDDLStatementAssert.java
@@ -20,10 +20,12 @@ package 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.statement.
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.ddl.DDLStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.doris.ddl.DorisCreateFunctionStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.doris.ddl.DorisResumeJobStatement;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.SQLParserTestCase;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.statement.ddl.dialect.doris.DorisResumeJobStatementTestCase;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.statement.ddl.standard.function.CreateFunctionStatementTestCase;
 
 /**
  * DDL statement assert for Doris.
@@ -41,6 +43,8 @@ public final class DorisDDLStatementAssert {
     public static void assertIs(final SQLCaseAssertContext assertContext, 
final DDLStatement actual, final SQLParserTestCase expected) {
         if (actual instanceof DorisResumeJobStatement) {
             DorisResumeJobStatementAssert.assertIs(assertContext, 
(DorisResumeJobStatement) actual, (DorisResumeJobStatementTestCase) expected);
+        } else if (actual instanceof DorisCreateFunctionStatement) {
+            DorisCreateFunctionStatementAssert.assertIs(assertContext, 
(DorisCreateFunctionStatement) actual, (CreateFunctionStatementTestCase) 
expected);
         }
     }
 }
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/dml/standard/type/SelectStatementAssert.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/dml/standard/type/SelectStatementAssert.java
index d8c183d79a3..0681719ca6d 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/dml/standard/type/SelectStatementAssert.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/statement/dml/standard/type/SelectStatementAssert.java
@@ -20,6 +20,7 @@ package 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.statement.
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.combine.CombineSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.outfile.OutfileSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.limit.LimitSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.predicate.LockSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.ModelSegment;
@@ -35,6 +36,7 @@ import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.lim
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.lock.LockClauseAssert;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.model.ModelClauseAssert;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.orderby.OrderByClauseAssert;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.outfile.OutfileClauseAssert;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.projection.ProjectionAssert;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.table.TableAssert;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.where.WhereClauseAssert;
@@ -76,6 +78,7 @@ public final class SelectStatementAssert {
         assertCombineClause(assertContext, actual, expected);
         assertModelClause(assertContext, actual, expected);
         assertIntoClause(assertContext, actual, expected);
+        assertOutfileClause(assertContext, actual, expected);
     }
     
     private static void assertWindowClause(final SQLCaseAssertContext 
assertContext, final SelectStatement actual, final SelectStatementTestCase 
expected) {
@@ -204,4 +207,14 @@ public final class SelectStatementAssert {
             TableAssert.assertIs(assertContext, intoSegment.get(), 
expected.getIntoClause());
         }
     }
+    
+    private static void assertOutfileClause(final SQLCaseAssertContext 
assertContext, final SelectStatement actual, final SelectStatementTestCase 
expected) {
+        Optional<OutfileSegment> outfileSegment = actual.getOutfile();
+        if (null == expected.getOutfileClause()) {
+            assertFalse(outfileSegment.isPresent(), 
assertContext.getText("Actual outfile segment should not exist."));
+        } else {
+            assertTrue(outfileSegment.isPresent(), 
assertContext.getText("Actual outfile segment should exist."));
+            OutfileClauseAssert.assertIs(assertContext, outfileSegment.get(), 
expected.getOutfileClause());
+        }
+    }
 }
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/outfile/ExpectedOutfileClause.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/outfile/ExpectedOutfileClause.java
new file mode 100644
index 00000000000..dbd9e0cf959
--- /dev/null
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/outfile/ExpectedOutfileClause.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.outfile;
+
+import lombok.Getter;
+import lombok.Setter;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.AbstractExpectedSQLSegment;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.distsql.ExpectedProperty;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Expected outfile clause.
+ */
+@Getter
+@Setter
+public final class ExpectedOutfileClause extends AbstractExpectedSQLSegment {
+    
+    @XmlAttribute(name = "file-path")
+    private String filePath;
+    
+    @XmlAttribute(name = "format")
+    private String format;
+    
+    @XmlElement(name = "property")
+    private List<ExpectedProperty> propertyList;
+    
+    /**
+     * Get properties as map.
+     *
+     * @return properties map
+     */
+    public Map<String, String> getProperties() {
+        if (null == propertyList || propertyList.isEmpty()) {
+            return null;
+        }
+        return 
propertyList.stream().collect(Collectors.toMap(ExpectedProperty::getKey, 
ExpectedProperty::getValue, (oldValue, newValue) -> newValue, 
LinkedHashMap::new));
+    }
+}
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/type/ExpectedDataTypeSegment.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/type/ExpectedDataTypeSegment.java
new file mode 100644
index 00000000000..8f4e12bf380
--- /dev/null
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/type/ExpectedDataTypeSegment.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.type;
+
+import lombok.Getter;
+import lombok.Setter;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.AbstractExpectedSQLSegment;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+/**
+ * Expected data type segment.
+ */
+@Getter
+@Setter
+public final class ExpectedDataTypeSegment extends AbstractExpectedSQLSegment {
+    
+    @XmlAttribute
+    private String name;
+}
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/function/CreateFunctionStatementTestCase.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/function/CreateFunctionStatementTestCase.java
index c4b20c365c2..3a79aaaf4f4 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/function/CreateFunctionStatementTestCase.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/ddl/standard/function/CreateFunctionStatementTestCase.java
@@ -20,11 +20,15 @@ package 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.
 import lombok.Getter;
 import lombok.Setter;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.SQLParserTestCase;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedExpression;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.distsql.ExpectedProperty;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.plsql.ExpectedDynamicSqlStatementExpressionSegment;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.plsql.ExpectedProcedureCallNameSegment;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.plsql.ExpectedRoutineName;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.plsql.ExpectedSQLStatementSegment;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.type.ExpectedDataTypeSegment;
 
+import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import java.util.LinkedList;
@@ -51,4 +55,31 @@ public final class CreateFunctionStatementTestCase extends 
SQLParserTestCase {
     @XmlElementWrapper(name = "dynamic-sql-statement-expressions")
     @XmlElement(name = "dynamic-sql-statement-expression")
     private List<ExpectedDynamicSqlStatementExpressionSegment> 
dynamicSqlStatementExpressions = new LinkedList<>();
+    
+    @XmlAttribute(name = "global")
+    private Boolean global;
+    
+    @XmlAttribute(name = "function-type")
+    private String functionType;
+    
+    @XmlElementWrapper(name = "parameter-data-types")
+    @XmlElement(name = "data-type")
+    private List<ExpectedDataTypeSegment> parameterDataTypes = new 
LinkedList<>();
+    
+    @XmlElement(name = "return-type")
+    private ExpectedDataTypeSegment returnType;
+    
+    @XmlElement(name = "intermediate-type")
+    private ExpectedDataTypeSegment intermediateType;
+    
+    @XmlElementWrapper(name = "with-parameters")
+    @XmlElement(name = "parameter")
+    private List<String> withParameters = new LinkedList<>();
+    
+    @XmlElement(name = "alias-expression")
+    private ExpectedExpression aliasExpression;
+    
+    @XmlElementWrapper(name = "properties")
+    @XmlElement(name = "property")
+    private List<ExpectedProperty> properties = new LinkedList<>();
 }
diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/dml/standard/SelectStatementTestCase.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/dml/standard/SelectStatementTestCase.java
index 505b162c2f8..ca9950c0a58 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/dml/standard/SelectStatementTestCase.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/statement/dml/standard/SelectStatementTestCase.java
@@ -25,6 +25,7 @@ import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.lock.ExpectedLockClause;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.model.ExpectedModelClause;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.orderby.ExpectedOrderByClause;
+import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.outfile.ExpectedOutfileClause;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.projection.ExpectedProjections;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.table.ExpectedTable;
 import 
org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.union.ExpectedCombine;
@@ -79,4 +80,7 @@ public final class SelectStatementTestCase extends 
SQLParserTestCase {
     
     @XmlElement(name = "into")
     private ExpectedTable intoClause;
+    
+    @XmlElement(name = "outfile")
+    private ExpectedOutfileClause outfileClause;
 }
diff --git a/test/it/parser/src/main/resources/case/ddl/create-function.xml 
b/test/it/parser/src/main/resources/case/ddl/create-function.xml
index f691fd3fa64..0314dea98fb 100644
--- a/test/it/parser/src/main/resources/case/ddl/create-function.xml
+++ b/test/it/parser/src/main/resources/case/ddl/create-function.xml
@@ -40,4 +40,116 @@
     <create-function sql-case-id="create_function_with_two_resources" />
     <create-function sql-case-id="create_function_with_three_resource_types" />
     <create-function 
sql-case-id="create_function_with_three_resource_types_and_db" />
+    <create-function sql-case-id="create_function_doris_udf_basic">
+        <function-name name="java_udf_add_one" start-index="16" 
stop-index="31" />
+        <parameter-data-types>
+            <data-type name="int" start-index="34" stop-index="36" />
+        </parameter-data-types>
+        <return-type name="int" start-index="47" stop-index="49" />
+        <properties>
+            <property key="file" 
value="file:///path/to/java-udf-demo-jar-with-dependencies.jar" />
+            <property key="symbol" value="org.apache.doris.udf.AddOne" />
+            <property key="always_nullable" value="true" />
+            <property key="type" value="JAVA_UDF" />
+        </properties>
+    </create-function>
+    <create-function sql-case-id="create_function_doris_aggregate" 
function-type="AGGREGATE">
+        <function-name name="simple_sum" start-index="26" stop-index="35" />
+        <parameter-data-types>
+            <data-type name="INT" start-index="38" stop-index="40" />
+        </parameter-data-types>
+        <return-type name="INT" start-index="50" stop-index="52" />
+        <properties>
+            <property key="file" value="file:///pathTo/java-udaf.jar" />
+            <property key="symbol" 
value="org.apache.doris.udf.demo.SimpleDemo" />
+            <property key="always_nullable" value="true" />
+            <property key="type" value="JAVA_UDF" />
+        </properties>
+    </create-function>
+    <create-function sql-case-id="create_function_doris_table_function" 
function-type="TABLES">
+        <function-name name="java_udtf" start-index="23" stop-index="31" />
+        <parameter-data-types>
+            <data-type name="string" start-index="34" stop-index="39" />
+            <data-type name="string" start-index="42" stop-index="47" />
+        </parameter-data-types>
+        <return-type name="array" start-index="58" stop-index="62" />
+        <properties>
+            <property key="file" value="file:///pathTo/java-udaf.jar" />
+            <property key="symbol" 
value="org.apache.doris.udf.demo.UDTFStringTest" />
+            <property key="always_nullable" value="true" />
+            <property key="type" value="JAVA_UDF" />
+        </properties>
+    </create-function>
+    <create-function sql-case-id="create_function_doris_alias" 
function-type="ALIAS">
+        <function-name name="id_masking" start-index="22" stop-index="31" />
+        <parameter-data-types>
+            <data-type name="INT" start-index="41" stop-index="43" />
+        </parameter-data-types>
+        <with-parameters>
+            <parameter>id</parameter>
+        </with-parameters>
+        <alias-expression start-index="60" stop-index="100">
+            <function start-index="60" stop-index="100" function-name="CONCAT" 
text="CONCAT(LEFT(id, 3), '****', RIGHT(id, 4))">
+                <parameter start-index="67" stop-index="77">
+                    <function start-index="67" stop-index="77" 
function-name="LEFT" text="LEFT(id, 3)">
+                        <parameter start-index="72" stop-index="73">
+                            <column start-index="72" stop-index="73" name="id" 
/>
+                        </parameter>
+                        <parameter start-index="76" stop-index="76">
+                            <literal-expression start-index="76" 
stop-index="76" value="3" />
+                        </parameter>
+                    </function>
+                </parameter>
+                <parameter start-index="80" stop-index="85">
+                    <literal-expression start-index="80" stop-index="85" 
value="****" />
+                </parameter>
+                <parameter start-index="88" stop-index="91">
+                    <function start-index="88" stop-index="99" 
function-name="RIGHT" text="RIGHT(id, 4)">
+                        <parameter start-index="94" stop-index="95">
+                            <column start-index="94" stop-index="95" name="id" 
/>
+                        </parameter>
+                        <parameter start-index="98" stop-index="98">
+                            <literal-expression start-index="98" 
stop-index="98" value="4" />
+                        </parameter>
+                    </function>
+                </parameter>
+            </function>
+        </alias-expression>
+    </create-function>
+    <create-function sql-case-id="create_function_doris_global_alias" 
global="true" function-type="ALIAS">
+        <function-name name="id_masking" start-index="29" stop-index="38" />
+        <parameter-data-types>
+            <data-type name="INT" start-index="48" stop-index="50" />
+        </parameter-data-types>
+        <with-parameters>
+            <parameter>id</parameter>
+        </with-parameters>
+        <alias-expression start-index="67" stop-index="107">
+            <function start-index="67" stop-index="107" function-name="CONCAT" 
text="CONCAT(LEFT(id, 3), '****', RIGHT(id, 4))">
+                <parameter start-index="74" stop-index="84">
+                    <function start-index="74" stop-index="84" 
function-name="LEFT" text="LEFT(id, 3)">
+                        <parameter start-index="79" stop-index="80">
+                            <column start-index="79" stop-index="80" name="id" 
/>
+                        </parameter>
+                        <parameter start-index="83" stop-index="83">
+                            <literal-expression start-index="83" 
stop-index="83" value="3" />
+                        </parameter>
+                    </function>
+                </parameter>
+                <parameter start-index="87" stop-index="92">
+                    <literal-expression start-index="87" stop-index="92" 
value="****" />
+                </parameter>
+                <parameter start-index="95" stop-index="106">
+                    <function start-index="95" stop-index="106" 
function-name="RIGHT" text="RIGHT(id, 4)">
+                        <parameter start-index="101" stop-index="102">
+                            <column start-index="101" stop-index="102" 
name="id" />
+                        </parameter>
+                        <parameter start-index="105" stop-index="105">
+                            <literal-expression start-index="105" 
stop-index="105" value="4" />
+                        </parameter>
+                    </function>
+                </parameter>
+            </function>
+        </alias-expression>
+    </create-function>
 </sql-parser-test-cases>
diff --git a/test/it/parser/src/main/resources/case/dml/select-into.xml 
b/test/it/parser/src/main/resources/case/dml/select-into.xml
index 77a4076a0a3..7569c48f039 100644
--- a/test/it/parser/src/main/resources/case/dml/select-into.xml
+++ b/test/it/parser/src/main/resources/case/dml/select-into.xml
@@ -363,4 +363,65 @@
             <column-item name="title" start-index="94" stop-index="98" 
order-direction="ASC"/>
         </order-by>
     </select>
+
+    <select sql-case-id="select_into_outfile_doris_basic">
+        <from start-index="14" stop-index="20">
+            <simple-table name="t_order" start-index="14" stop-index="20" />
+        </from>
+        <projections distinct-row="false" start-index="7" stop-index="7">
+            <shorthand-projection start-index="7" stop-index="7" />
+        </projections>
+        <outfile start-index="22" stop-index="58" 
file-path="hdfs://path/to/result_" />
+    </select>
+
+    <select sql-case-id="select_into_outfile_doris_with_format">
+        <from start-index="14" stop-index="20">
+            <simple-table name="t_order" start-index="14" stop-index="20" />
+        </from>
+        <projections distinct-row="false" start-index="7" stop-index="7">
+            <shorthand-projection start-index="7" stop-index="7" />
+        </projections>
+        <outfile start-index="22" stop-index="72" 
file-path="hdfs://path/to/result_" format="CSV" />
+    </select>
+
+    <select sql-case-id="select_into_outfile_doris_with_properties">
+        <from start-index="14" stop-index="20">
+            <simple-table name="t_order" start-index="14" stop-index="20" />
+        </from>
+        <projections distinct-row="false" start-index="7" stop-index="7">
+            <shorthand-projection start-index="7" stop-index="7" />
+        </projections>
+        <outfile start-index="22" stop-index="121" 
file-path="hdfs://path/to/result_">
+            <property key="column_separator" value="," />
+            <property key="line_delimiter" value="\n" />
+        </outfile>
+    </select>
+
+    <select sql-case-id="select_into_outfile_doris_full">
+        <from start-index="14" stop-index="20">
+            <simple-table name="t_order" start-index="14" stop-index="20" />
+        </from>
+        <projections distinct-row="false" start-index="7" stop-index="7">
+            <shorthand-projection start-index="7" stop-index="7" />
+        </projections>
+        <outfile start-index="22" stop-index="166" 
file-path="hdfs://path/to/result_" format="CSV">
+            <property key="broker.name" value="my_broker" />
+            <property key="column_separator" value="," />
+            <property key="max_file_size" value="100MB" />
+        </outfile>
+    </select>
+
+    <select sql-case-id="select_into_outfile_doris_with_parquet">
+        <from start-index="14" stop-index="20">
+            <simple-table name="t_order" start-index="14" stop-index="20" />
+        </from>
+        <projections distinct-row="false" start-index="7" stop-index="7">
+            <shorthand-projection start-index="7" stop-index="7" />
+        </projections>
+        <outfile start-index="22" stop-index="178" 
file-path="s3://bucket/result_" format="PARQUET">
+            <property key="s3.endpoint" value="http://s3.amazonaws.com"; />
+            <property key="s3.access_key" value="xxx" />
+            <property key="s3.secret_key" value="yyy" />
+        </outfile>
+    </select>
 </sql-parser-test-cases>
diff --git 
a/test/it/parser/src/main/resources/case/dml/select-special-function.xml 
b/test/it/parser/src/main/resources/case/dml/select-special-function.xml
index 627bbb0e417..8291c760b5c 100644
--- a/test/it/parser/src/main/resources/case/dml/select-special-function.xml
+++ b/test/it/parser/src/main/resources/case/dml/select-special-function.xml
@@ -5308,4 +5308,38 @@
             <simple-table name="DUAL" start-index="74" stop-index="77"/>
         </from>
     </select>
+
+    <select sql-case-id="select_regexp_function" db-types="Doris">
+        <projections start-index="7" stop-index="41">
+            <expression-projection text="REGEXP('billie eillish', '^billie')" 
start-index="7" stop-index="41">
+                <expr>
+                    <function function-name="REGEXP" start-index="7" 
stop-index="41" text="REGEXP('billie eillish', '^billie')">
+                        <parameter>
+                            <literal-expression value="billie eillish" 
start-index="14" stop-index="29" />
+                        </parameter>
+                        <parameter>
+                            <literal-expression value="^billie" 
start-index="32" stop-index="40" />
+                        </parameter>
+                    </function>
+                </expr>
+            </expression-projection>
+        </projections>
+    </select>
+
+    <select sql-case-id="select_regexp_function_with_null" db-types="Doris">
+        <projections start-index="7" stop-index="29">
+            <expression-projection text="REGEXP(NULL, '^billie')" 
start-index="7" stop-index="29">
+                <expr>
+                    <function function-name="REGEXP" start-index="7" 
stop-index="29" text="REGEXP(NULL, '^billie')">
+                        <parameter>
+                            <literal-expression value="null" start-index="14" 
stop-index="17" />
+                        </parameter>
+                        <parameter>
+                            <literal-expression value="^billie" 
start-index="20" stop-index="28" />
+                        </parameter>
+                    </function>
+                </expr>
+            </expression-projection>
+        </projections>
+    </select>
 </sql-parser-test-cases>
diff --git 
a/test/it/parser/src/main/resources/sql/supported/ddl/create-function.xml 
b/test/it/parser/src/main/resources/sql/supported/ddl/create-function.xml
index 3a8f71c8768..c6e920cfda4 100644
--- a/test/it/parser/src/main/resources/sql/supported/ddl/create-function.xml
+++ b/test/it/parser/src/main/resources/sql/supported/ddl/create-function.xml
@@ -83,4 +83,9 @@
     <sql-case id="create_function_with_two_resources" value="CREATE FUNCTION 
process_data AS 'com.example.DataProcessor' USING JAR 
'hdfs:///udfs/data-processor.jar', FILE 'hdfs:///udfs/config.properties';" 
db-types="Hive" />
     <sql-case id="create_function_with_three_resource_types" value="CREATE 
FUNCTION process_multi_data AS 'com.example.MultiDataProcessor' USING JAR 
'hdfs:///udfs/core-processor.jar', FILE 
'hdfs:///configs/processing-rules.json', ARCHIVE 
'hdfs:///datasets/sample-data.tar.gz';" db-types="Hive" />
     <sql-case id="create_function_with_three_resource_types_and_db" 
value="CREATE FUNCTION db1.process_multi_data AS 
'com.example.MultiDataProcessor' USING JAR 'hdfs:///udfs/core-processor.jar', 
FILE 'hdfs:///configs/processing-rules.json', ARCHIVE 
'hdfs:///datasets/sample-data.tar.gz';" db-types="Hive" />
+    <sql-case id="create_function_doris_udf_basic" value="CREATE FUNCTION 
java_udf_add_one(int) RETURNS int PROPERTIES 
(&quot;file&quot;=&quot;file:///path/to/java-udf-demo-jar-with-dependencies.jar&quot;,
 &quot;symbol&quot;=&quot;org.apache.doris.udf.AddOne&quot;, 
&quot;always_nullable&quot;=&quot;true&quot;, 
&quot;type&quot;=&quot;JAVA_UDF&quot;);" db-types="Doris" />
+    <sql-case id="create_function_doris_aggregate" value="CREATE AGGREGATE 
FUNCTION simple_sum(INT) RETURNS INT PROPERTIES 
(&quot;file&quot;=&quot;file:///pathTo/java-udaf.jar&quot;, 
&quot;symbol&quot;=&quot;org.apache.doris.udf.demo.SimpleDemo&quot;, 
&quot;always_nullable&quot;=&quot;true&quot;, 
&quot;type&quot;=&quot;JAVA_UDF&quot;);" db-types="Doris" />
+    <sql-case id="create_function_doris_table_function" value="CREATE TABLES 
FUNCTION java_udtf(string, string) RETURNS array PROPERTIES 
(&quot;file&quot;=&quot;file:///pathTo/java-udaf.jar&quot;, 
&quot;symbol&quot;=&quot;org.apache.doris.udf.demo.UDTFStringTest&quot;, 
&quot;always_nullable&quot;=&quot;true&quot;, 
&quot;type&quot;=&quot;JAVA_UDF&quot;);" db-types="Doris" />
+    <sql-case id="create_function_doris_alias" value="CREATE ALIAS FUNCTION 
id_masking(INT) WITH PARAMETER(id) AS CONCAT(LEFT(id, 3), '****', RIGHT(id, 
4));" db-types="Doris" />
+    <sql-case id="create_function_doris_global_alias" value="CREATE GLOBAL 
ALIAS FUNCTION id_masking(INT) WITH PARAMETER(id) AS CONCAT(LEFT(id, 3), 
'****', RIGHT(id, 4));" db-types="Doris" />
 </sql-cases>
diff --git 
a/test/it/parser/src/main/resources/sql/supported/dml/select-into.xml 
b/test/it/parser/src/main/resources/sql/supported/dml/select-into.xml
index c03fc5e1fbf..07ccfe0e06a 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select-into.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select-into.xml
@@ -33,4 +33,9 @@
     <sql-case id="select_into_table_before_from" value="SELECT * INTO 
dbo.NewProducts FROM Production.Product WHERE ListPrice &gt; $25 AND ListPrice 
&lt; $100;" db-types="SQLServer"/>
     <sql-case id="select_into_simple_table" value="SELECT film_id, title, 
rental_rate INTO TABLE film_r FROM film WHERE rating = 'R' AND rental_duration 
= 5 ORDER BY title" db-types="PostgreSQL, openGauss"/>
     <sql-case id="select_into_temp_table" value="SELECT film_id, title, length 
INTO TEMP TABLE short_film FROM film WHERE length &lt; 60 ORDER BY title" 
db-types="PostgreSQL, openGauss"/>
+    <sql-case id="select_into_outfile_doris_basic" value="SELECT * FROM 
t_order INTO OUTFILE 'hdfs://path/to/result_'" db-types="Doris"/>
+    <sql-case id="select_into_outfile_doris_with_format" value="SELECT * FROM 
t_order INTO OUTFILE 'hdfs://path/to/result_' FORMAT AS CSV" db-types="Doris"/>
+    <sql-case id="select_into_outfile_doris_with_properties" value="SELECT * 
FROM t_order INTO OUTFILE 'hdfs://path/to/result_' PROPERTIES 
('column_separator' = ',', 'line_delimiter' = '\n')" db-types="Doris"/>
+    <sql-case id="select_into_outfile_doris_full" value="SELECT * FROM t_order 
INTO OUTFILE 'hdfs://path/to/result_' FORMAT AS CSV PROPERTIES ('broker.name' = 
'my_broker', 'column_separator' = ',', 'max_file_size' = '100MB')" 
db-types="Doris"/>
+    <sql-case id="select_into_outfile_doris_with_parquet" value="SELECT * FROM 
t_order INTO OUTFILE 's3://bucket/result_' FORMAT AS PARQUET PROPERTIES 
('s3.endpoint' = 'http://s3.amazonaws.com', 's3.access_key' = 'xxx', 
's3.secret_key' = 'yyy')" db-types="Doris"/>
 </sql-cases>
diff --git 
a/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
 
b/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
index 85cc26abb43..fa71c12b293 100644
--- 
a/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
+++ 
b/test/it/parser/src/main/resources/sql/supported/dml/select-special-function.xml
@@ -305,4 +305,6 @@
     <sql-case id="select_pi" value="SELECT PI()" db-types="MySQL" />
     <sql-case id="select_regexp" value="SELECT 'a' REGEXP '^[a-d]'" 
db-types="MySQL" />
     <sql-case id="select_with_json_object_function" value="SELECT 
JSON_OBJECT('name' VALUE 'John', 'age' VALUE 30) AS json_data FROM DUAL" 
db-types="Oracle" />
+    <sql-case id="select_regexp_function" value="SELECT REGEXP('billie 
eillish', '^billie')" db-types="Doris" />
+    <sql-case id="select_regexp_function_with_null" value="SELECT REGEXP(NULL, 
'^billie')" db-types="Doris" />
 </sql-cases>

Reply via email to