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
("file"="file:///path/to/java-udf-demo-jar-with-dependencies.jar",
"symbol"="org.apache.doris.udf.AddOne",
"always_nullable"="true",
"type"="JAVA_UDF");" db-types="Doris" />
+ <sql-case id="create_function_doris_aggregate" value="CREATE AGGREGATE
FUNCTION simple_sum(INT) RETURNS INT PROPERTIES
("file"="file:///pathTo/java-udaf.jar",
"symbol"="org.apache.doris.udf.demo.SimpleDemo",
"always_nullable"="true",
"type"="JAVA_UDF");" db-types="Doris" />
+ <sql-case id="create_function_doris_table_function" value="CREATE TABLES
FUNCTION java_udtf(string, string) RETURNS array PROPERTIES
("file"="file:///pathTo/java-udaf.jar",
"symbol"="org.apache.doris.udf.demo.UDTFStringTest",
"always_nullable"="true",
"type"="JAVA_UDF");" 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 > $25 AND ListPrice
< $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 < 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>