This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new f2fd66ad3b5 [feature-wip](nereids) Make nereids more compatible with
spark-sql syntax. (#27231)
f2fd66ad3b5 is described below
commit f2fd66ad3b5c82b7277491ccec84d19f807b6db3
Author: Xiangyu Wang <[email protected]>
AuthorDate: Mon Dec 11 11:16:53 2023 +0800
[feature-wip](nereids) Make nereids more compatible with spark-sql syntax.
(#27231)
**Thanks for** pr #21855 to provide a wonderful reference.
Maybe it is very difficult and **cost-expensive** to implement **a
comprehensive logical plan adapter**, maybe there is just some small syntax
variations between doris and some other engines (such as hive/spark), so we can
just **focus on** the **difference** here.
This pr mainly focus on the **syntax difference between doris and
spark-sql**. For instance, do some function tranformations and override some
syntax validations.
- add a dialect named `spark_sql`
- move method `NereidsParser#parseSQLWithDialect` to `TrinoParser`
- extract some `FnCallTransformer`/`FnCallTransformers` classes, so we can
reuse the logic about the function transformers
- allow derived tables without alias when we set dialect to
`spark_sql`(legacy and nereids parser are both supported)
- add some function transformers for hive/spark built-in functions
### Test case (from our online doris cluster)
- Test derived table without alias
```sql
MySQL [(none)]> show variables like '%dialect%';
+---------------+-------+---------------+---------+
| Variable_name | Value | Default_Value | Changed |
+---------------+-------+---------------+---------+
| sql_dialect | spark_sql | doris | 1 |
+---------------+-------+---------------+---------+
1 row in set (0.01 sec)
MySQL [(none)]> select * from (select 1);
+------+
| 1 |
+------+
| 1 |
+------+
1 row in set (0.03 sec)
MySQL [(none)]> select __auto_generated_subquery_name.a from (select 1 as
a);
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.03 sec)
MySQL [(none)]> set sql_dialect=doris;
Query OK, 0 rows affected (0.02 sec)
MySQL [(none)]> select * from (select 1);
ERROR 1248 (42000): errCode = 2, detailMessage = Every derived table must
have its own alias
MySQL [(none)]>
```
- Test spark-sql/hive built-in functions
```sql
MySQL [(none)]> show global functions;
Empty set (0.01 sec)
MySQL [(none)]> show variables like '%dialect%';
+---------------+-------+---------------+---------+
| Variable_name | Value | Default_Value | Changed |
+---------------+-------+---------------+---------+
| sql_dialect | spark_sql | doris | 1 |
+---------------+-------+---------------+---------+
1 row in set (0.01 sec)
MySQL [(none)]> select get_json_object('{"a":"b"}', '$.a');
+----------------------------------+
| json_extract('{"a":"b"}', '$.a') |
+----------------------------------+
| "b" |
+----------------------------------+
1 row in set (0.04 sec)
MySQL [(none)]> select split("a b c", " ");
+-------------------------------+
| split_by_string('a b c', ' ') |
+-------------------------------+
| ["a", "b", "c"] |
+-------------------------------+
1 row in set (1.17 sec)
```
---
.../org/apache/doris/analysis/InlineViewRef.java | 11 ++-
.../nereids/analyzer/PlaceholderExpression.java | 2 +-
.../{trino => }/AbstractFnCallTransformer.java | 3 +-
...ormers.java => AbstractFnCallTransformers.java} | 76 ++++++--------
...ansformer.java => CommonFnCallTransformer.java} | 11 +--
.../doris/nereids/parser/LogicalPlanBuilder.java | 2 +-
.../parser/LogicalPlanBuilderAssistant.java | 2 +-
.../apache/doris/nereids/parser/NereidsParser.java | 109 ++++++++++-----------
.../apache/doris/nereids/parser/ParseDialect.java | 22 ++++-
.../parser/spark/SparkSql3FnCallTransformers.java | 71 ++++++++++++++
.../parser/spark/SparkSql3LogicalPlanBuilder.java | 88 +++++++++++++++++
.../trino/ComplexTrinoFnCallTransformer.java | 2 +
.../parser/trino/TrinoFnCallTransformers.java | 94 +++---------------
...noBuilder.java => TrinoLogicalPlanBuilder.java} | 4 +-
.../doris/nereids/parser/trino/TrinoParser.java | 62 +++++++++++-
.../java/org/apache/doris/qe/ConnectProcessor.java | 2 +-
.../java/org/apache/doris/qe/StmtExecutor.java | 2 +-
.../doris/nereids/parser/NereidsParserTest.java | 20 +++-
.../nereids/parser/spark/FnTransformTest.java | 55 +++++++++++
.../nereids/util/TrinoDialectPlanParseChecker.java | 3 +-
20 files changed, 433 insertions(+), 208 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/analysis/InlineViewRef.java
b/fe/fe-core/src/main/java/org/apache/doris/analysis/InlineViewRef.java
index d331e090b1b..5cc6a363cff 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/InlineViewRef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/InlineViewRef.java
@@ -27,6 +27,9 @@ import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.ErrorCode;
import org.apache.doris.common.ErrorReport;
import org.apache.doris.common.UserException;
+import org.apache.doris.nereids.parser.ParseDialect;
+import org.apache.doris.nereids.parser.spark.SparkSql3LogicalPlanBuilder;
+import org.apache.doris.qe.ConnectContext;
import org.apache.doris.rewrite.ExprRewriter;
import org.apache.doris.thrift.TNullSide;
@@ -194,7 +197,13 @@ public class InlineViewRef extends TableRef {
}
if (view == null && !hasExplicitAlias()) {
-
ErrorReport.reportAnalysisException(ErrorCode.ERR_DERIVED_MUST_HAVE_ALIAS);
+ String dialect =
ConnectContext.get().getSessionVariable().getSqlDialect();
+ ParseDialect.Dialect sqlDialect =
ParseDialect.Dialect.getByName(dialect);
+ if (ParseDialect.Dialect.SPARK_SQL != sqlDialect) {
+
ErrorReport.reportAnalysisException(ErrorCode.ERR_DERIVED_MUST_HAVE_ALIAS);
+ }
+ hasExplicitAlias = true;
+ aliases = new String[] {
SparkSql3LogicalPlanBuilder.DEFAULT_TABLE_ALIAS };
}
// Analyze the inline view query statement with its own analyzer
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/PlaceholderExpression.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/PlaceholderExpression.java
index 50af01dd2ef..8f069b25694 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/PlaceholderExpression.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/PlaceholderExpression.java
@@ -17,7 +17,7 @@
package org.apache.doris.nereids.analyzer;
-import
org.apache.doris.nereids.parser.trino.TrinoFnCallTransformer.PlaceholderCollector;
+import
org.apache.doris.nereids.parser.CommonFnCallTransformer.PlaceholderCollector;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/AbstractFnCallTransformer.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/AbstractFnCallTransformer.java
similarity index 94%
rename from
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/AbstractFnCallTransformer.java
rename to
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/AbstractFnCallTransformer.java
index 4bdb5bcd080..fe8167f080e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/AbstractFnCallTransformer.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/AbstractFnCallTransformer.java
@@ -15,9 +15,8 @@
// specific language governing permissions and limitations
// under the License.
-package org.apache.doris.nereids.parser.trino;
+package org.apache.doris.nereids.parser;
-import org.apache.doris.nereids.parser.ParserContext;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.Function;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformers.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/AbstractFnCallTransformers.java
similarity index 59%
copy from
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformers.java
copy to
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/AbstractFnCallTransformers.java
index a5e7f07fe17..75b5f87263c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformers.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/AbstractFnCallTransformers.java
@@ -15,53 +15,50 @@
// specific language governing permissions and limitations
// under the License.
-package org.apache.doris.nereids.parser.trino;
+package org.apache.doris.nereids.parser;
-import org.apache.doris.nereids.analyzer.PlaceholderExpression;
import org.apache.doris.nereids.analyzer.UnboundFunction;
-import org.apache.doris.nereids.parser.ParserContext;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import java.util.List;
import java.util.stream.Collectors;
/**
- * The builder and factory for {@link
org.apache.doris.nereids.parser.trino.TrinoFnCallTransformer},
+ * The abstract holder for {@link AbstractFnCallTransformer},
* and supply transform facade ability.
*/
-public class TrinoFnCallTransformers {
+public abstract class AbstractFnCallTransformers {
- private static ImmutableListMultimap<String, AbstractFnCallTransformer>
TRANSFORMER_MAP;
- private static ImmutableListMultimap<String, AbstractFnCallTransformer>
COMPLEX_TRANSFORMER_MAP;
- private static final ImmutableListMultimap.Builder<String,
AbstractFnCallTransformer> transformerBuilder =
+ private final ImmutableListMultimap<String, AbstractFnCallTransformer>
transformerMap;
+ private final ImmutableListMultimap<String, AbstractFnCallTransformer>
complexTransformerMap;
+ private final ImmutableListMultimap.Builder<String,
AbstractFnCallTransformer> transformerBuilder =
ImmutableListMultimap.builder();
- private static final ImmutableListMultimap.Builder<String,
AbstractFnCallTransformer> complexTransformerBuilder =
+ private final ImmutableListMultimap.Builder<String,
AbstractFnCallTransformer> complexTransformerBuilder =
ImmutableListMultimap.builder();
- static {
+ protected AbstractFnCallTransformers() {
registerTransformers();
+ transformerMap = transformerBuilder.build();
registerComplexTransformers();
- }
-
- private TrinoFnCallTransformers() {
+ // build complex transformer map in the end
+ complexTransformerMap = complexTransformerBuilder.build();
}
/**
* Function transform facade
*/
- public static Function transform(String sourceFnName, List<Expression>
sourceFnTransformedArguments,
+ public Function transform(String sourceFnName, List<Expression>
sourceFnTransformedArguments,
ParserContext context) {
List<AbstractFnCallTransformer> transformers =
getTransformers(sourceFnName);
return doTransform(transformers, sourceFnName,
sourceFnTransformedArguments, context);
}
- private static Function doTransform(List<AbstractFnCallTransformer>
transformers,
+ private Function doTransform(List<AbstractFnCallTransformer> transformers,
String sourceFnName,
List<Expression> sourceFnTransformedArguments,
ParserContext context) {
@@ -78,36 +75,7 @@ public class TrinoFnCallTransformers {
return null;
}
- private static List<AbstractFnCallTransformer> getTransformers(String
sourceFnName) {
- ImmutableList<AbstractFnCallTransformer> fnCallTransformers =
- TRANSFORMER_MAP.get(sourceFnName);
- ImmutableList<AbstractFnCallTransformer> complexFnCallTransformers =
- COMPLEX_TRANSFORMER_MAP.get(sourceFnName);
- return ImmutableList.copyOf(Iterables.concat(fnCallTransformers,
complexFnCallTransformers));
- }
-
- private static void registerTransformers() {
- registerStringFunctionTransformer();
- // TODO: add other function transformer
- // build transformer map in the end
- TRANSFORMER_MAP = transformerBuilder.build();
- }
-
- private static void registerComplexTransformers() {
- DateDiffFnCallTransformer dateDiffFnCallTransformer = new
DateDiffFnCallTransformer();
- doRegister(dateDiffFnCallTransformer.getSourceFnName(),
dateDiffFnCallTransformer);
- // TODO: add other complex function transformer
- // build complex transformer map in the end
- COMPLEX_TRANSFORMER_MAP = complexTransformerBuilder.build();
- }
-
- private static void registerStringFunctionTransformer() {
- doRegister("codepoint", 1, "ascii",
- Lists.newArrayList(PlaceholderExpression.of(Expression.class,
1)), false);
- // TODO: add other string function transformer
- }
-
- private static void doRegister(
+ protected void doRegister(
String sourceFnNme,
int sourceFnArgumentsNum,
String targetFnName,
@@ -118,13 +86,25 @@ public class TrinoFnCallTransformers {
.stream()
.map(each -> (Expression) each)
.collect(Collectors.toList());
- transformerBuilder.put(sourceFnNme, new TrinoFnCallTransformer(new
UnboundFunction(
+ transformerBuilder.put(sourceFnNme, new CommonFnCallTransformer(new
UnboundFunction(
targetFnName, castedTargetFnArguments), variableArgument,
sourceFnArgumentsNum));
}
- private static void doRegister(
+ protected void doRegister(
String sourceFnNme,
AbstractFnCallTransformer transformer) {
complexTransformerBuilder.put(sourceFnNme, transformer);
}
+
+ private List<AbstractFnCallTransformer> getTransformers(String
sourceFnName) {
+ ImmutableList<AbstractFnCallTransformer> fnCallTransformers =
+ transformerMap.get(sourceFnName);
+ ImmutableList<AbstractFnCallTransformer> complexFnCallTransformers =
+ complexTransformerMap.get(sourceFnName);
+ return ImmutableList.copyOf(Iterables.concat(fnCallTransformers,
complexFnCallTransformers));
+ }
+
+ protected abstract void registerTransformers();
+
+ protected abstract void registerComplexTransformers();
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformer.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/CommonFnCallTransformer.java
similarity index 93%
rename from
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformer.java
rename to
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/CommonFnCallTransformer.java
index 7ed99f4c48c..872c21a71e5 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformer.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/CommonFnCallTransformer.java
@@ -15,11 +15,10 @@
// specific language governing permissions and limitations
// under the License.
-package org.apache.doris.nereids.parser.trino;
+package org.apache.doris.nereids.parser;
import org.apache.doris.nereids.analyzer.PlaceholderExpression;
import org.apache.doris.nereids.analyzer.UnboundFunction;
-import org.apache.doris.nereids.parser.ParserContext;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.Function;
import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
@@ -31,7 +30,7 @@ import java.util.stream.Collectors;
/**
* Trino function transformer
*/
-public class TrinoFnCallTransformer extends AbstractFnCallTransformer {
+public class CommonFnCallTransformer extends AbstractFnCallTransformer {
private final UnboundFunction targetFunction;
private final List<PlaceholderExpression> targetArguments;
private final boolean variableArgument;
@@ -40,9 +39,9 @@ public class TrinoFnCallTransformer extends
AbstractFnCallTransformer {
/**
* Trino function transformer, mostly this handle common function.
*/
- public TrinoFnCallTransformer(UnboundFunction targetFunction,
- boolean variableArgument,
- int sourceArgumentsNum) {
+ public CommonFnCallTransformer(UnboundFunction targetFunction,
+ boolean variableArgument,
+ int sourceArgumentsNum) {
this.targetFunction = targetFunction;
this.variableArgument = variableArgument;
this.sourceArgumentsNum = sourceArgumentsNum;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index cf9d591127c..96f6c4322d1 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -894,7 +894,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
/**
* process lateral view, add a {@link
org.apache.doris.nereids.trees.plans.logical.LogicalGenerate} on plan.
*/
- private LogicalPlan withGenerate(LogicalPlan plan, LateralViewContext ctx)
{
+ protected LogicalPlan withGenerate(LogicalPlan plan, LateralViewContext
ctx) {
if (ctx.LATERAL() == null) {
return plan;
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderAssistant.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderAssistant.java
index 606fd7a159a..9179667be4e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderAssistant.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderAssistant.java
@@ -31,7 +31,7 @@ import java.math.BigInteger;
/**
* Logical plan builder assistant for buildIn dialect and other dialect.
* The same logical in {@link
org.apache.doris.nereids.parser.LogicalPlanBuilder}
- * and {@link org.apache.doris.nereids.parser.trino.LogicalPlanTrinoBuilder}
can be
+ * and {@link org.apache.doris.nereids.parser.trino.TrinoLogicalPlanBuilder}
can be
* extracted to here.
*/
public class LogicalPlanBuilderAssistant {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java
index 9e5506cc99c..d973aab1eb5 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/NereidsParser.java
@@ -22,9 +22,8 @@ import org.apache.doris.common.Pair;
import org.apache.doris.nereids.DorisLexer;
import org.apache.doris.nereids.DorisParser;
import org.apache.doris.nereids.StatementContext;
-import org.apache.doris.nereids.exceptions.UnsupportedDialectException;
import org.apache.doris.nereids.glue.LogicalPlanAdapter;
-import org.apache.doris.nereids.parser.trino.LogicalPlanTrinoBuilder;
+import org.apache.doris.nereids.parser.spark.SparkSql3LogicalPlanBuilder;
import org.apache.doris.nereids.parser.trino.TrinoParser;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
@@ -37,14 +36,14 @@ import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.function.Function;
+import javax.annotation.Nullable;
/**
* Sql parser, convert sql DSL to logical plan.
@@ -59,45 +58,43 @@ public class NereidsParser {
* see <a
href="https://dev.mysql.com/doc/internals/en/com-set-option.html">docs</a> for
more information.
*/
public List<StatementBase> parseSQL(String originStr) {
- List<Pair<LogicalPlan, StatementContext>> logicalPlans =
parseMultiple(originStr);
- List<StatementBase> statementBases = Lists.newArrayList();
- for (Pair<LogicalPlan, StatementContext> parsedPlanToContext :
logicalPlans) {
- statementBases.add(new
LogicalPlanAdapter(parsedPlanToContext.first, parsedPlanToContext.second));
- }
- return statementBases;
+ return parseSQL(originStr, (LogicalPlanBuilder) null);
}
/**
* ParseSQL with dialect.
*/
public List<StatementBase> parseSQL(String sql, SessionVariable
sessionVariable) {
- if (ParseDialect.TRINO_395.getDialect().getDialectName()
- .equalsIgnoreCase(sessionVariable.getSqlDialect())) {
- return parseSQLWithDialect(sql, sessionVariable);
- } else {
- return parseSQL(sql);
- }
+ @Nullable ParseDialect.Dialect sqlDialect =
ParseDialect.Dialect.getByName(sessionVariable.getSqlDialect());
+ return parseSQLWithDialect(sql, sqlDialect, sessionVariable);
}
- private List<StatementBase> parseSQLWithDialect(String sql,
SessionVariable sessionVariable) {
- final List<StatementBase> logicalPlans = new ArrayList<>();
- try {
- io.trino.sql.parser.StatementSplitter splitter = new
io.trino.sql.parser.StatementSplitter(sql);
- ParserContext parserContext = new
ParserContext(ParseDialect.TRINO_395);
- StatementContext statementContext = new StatementContext();
- for (io.trino.sql.parser.StatementSplitter.Statement statement :
splitter.getCompleteStatements()) {
- Object parsedPlan =
parseSingleWithDialect(statement.statement(), parserContext);
- logicalPlans.add(parsedPlan == null
- ? null : new LogicalPlanAdapter((LogicalPlan)
parsedPlan, statementContext));
- }
- } catch (io.trino.sql.parser.ParsingException |
UnsupportedDialectException e) {
- LOG.debug("Failed to parse logical plan from trino, sql is :{}",
sql, e);
- return parseSQL(sql);
+ private List<StatementBase> parseSQL(String originStr, @Nullable
LogicalPlanBuilder logicalPlanBuilder) {
+ List<Pair<LogicalPlan, StatementContext>> logicalPlans =
parseMultiple(originStr, logicalPlanBuilder);
+ List<StatementBase> statementBases = Lists.newArrayList();
+ for (Pair<LogicalPlan, StatementContext> parsedPlanToContext :
logicalPlans) {
+ statementBases.add(new
LogicalPlanAdapter(parsedPlanToContext.first, parsedPlanToContext.second));
}
- if (logicalPlans.isEmpty() ||
logicalPlans.stream().anyMatch(Objects::isNull)) {
- return parseSQL(sql);
+ return statementBases;
+ }
+
+ private List<StatementBase> parseSQLWithDialect(String sql,
+ @Nullable
ParseDialect.Dialect sqlDialect,
+ SessionVariable
sessionVariable) {
+ switch (sqlDialect) {
+ case TRINO:
+ final List<StatementBase> logicalPlans =
TrinoParser.parse(sql, sessionVariable);
+ if (CollectionUtils.isEmpty(logicalPlans)) {
+ return parseSQL(sql);
+ }
+ return logicalPlans;
+
+ case SPARK_SQL:
+ return parseSQL(sql, new SparkSql3LogicalPlanBuilder());
+
+ default:
+ return parseSQL(sql);
}
- return logicalPlans;
}
/**
@@ -107,11 +104,26 @@ public class NereidsParser {
* @return logical plan
*/
public LogicalPlan parseSingle(String sql) {
- return parse(sql, DorisParser::singleStatement);
+ return parseSingle(sql, null);
+ }
+
+ /**
+ * parse sql DSL string.
+ *
+ * @param sql sql string
+ * @return logical plan
+ */
+ public LogicalPlan parseSingle(String sql, @Nullable LogicalPlanBuilder
logicalPlanBuilder) {
+ return parse(sql, logicalPlanBuilder, DorisParser::singleStatement);
}
public List<Pair<LogicalPlan, StatementContext>> parseMultiple(String sql)
{
- return parse(sql, DorisParser::multiStatements);
+ return parseMultiple(sql, null);
+ }
+
+ public List<Pair<LogicalPlan, StatementContext>> parseMultiple(String sql,
+ @Nullable
LogicalPlanBuilder logicalPlanBuilder) {
+ return parse(sql, logicalPlanBuilder, DorisParser::multiStatements);
}
public Expression parseExpression(String expression) {
@@ -127,28 +139,15 @@ public class NereidsParser {
}
private <T> T parse(String sql, Function<DorisParser, ParserRuleContext>
parseFunction) {
- ParserRuleContext tree = toAst(sql, parseFunction);
- LogicalPlanBuilder logicalPlanBuilder = new LogicalPlanBuilder();
- return (T) logicalPlanBuilder.visit(tree);
+ return parse(sql, null, parseFunction);
}
- /**
- * Parse dialect sql.
- *
- * @param sql sql string
- * @param parserContext parse context
- * @return logical plan
- */
- public <T> T parseSingleWithDialect(String sql, ParserContext
parserContext) {
- if (ParseDialect.TRINO_395.equals(parserContext.getParserDialect())) {
- io.trino.sql.tree.Statement statement = TrinoParser.parse(sql);
- return (T) new LogicalPlanTrinoBuilder().visit(statement,
parserContext);
- } else {
- LOG.debug("Failed to parse logical plan, the dialect name is {},
version is {}",
-
parserContext.getParserDialect().getDialect().getDialectName(),
- parserContext.getParserDialect().getVersion());
- throw new
UnsupportedDialectException(parserContext.getParserDialect());
- }
+ private <T> T parse(String sql, @Nullable LogicalPlanBuilder
logicalPlanBuilder,
+ Function<DorisParser, ParserRuleContext>
parseFunction) {
+ ParserRuleContext tree = toAst(sql, parseFunction);
+ LogicalPlanBuilder realLogicalPlanBuilder = logicalPlanBuilder == null
+ ? new LogicalPlanBuilder() : logicalPlanBuilder;
+ return (T) realLogicalPlanBuilder.visit(tree);
}
private ParserRuleContext toAst(String sql, Function<DorisParser,
ParserRuleContext> parseFunction) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/ParseDialect.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/ParseDialect.java
index e4255ff355c..93eeffba172 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/ParseDialect.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/ParseDialect.java
@@ -17,6 +17,8 @@
package org.apache.doris.nereids.parser;
+import javax.annotation.Nullable;
+
/**
* ParseDialect enum, maybe support other dialect.
*/
@@ -29,7 +31,11 @@ public enum ParseDialect {
/**
* Doris parser and it's version is 2.0.0.
*/
- DORIS_2_ALL(Dialect.DORIS, Version.DORIS_2_ALL);
+ DORIS_2_ALL(Dialect.DORIS, Version.DORIS_2_ALL),
+ /**
+ * Spark parser and it's version is 3.x.
+ */
+ SPARK_SQL_3_ALL(Dialect.SPARK_SQL, Version.SPARK_SQL_3_ALL);
private final Dialect dialect;
private final Version version;
@@ -58,7 +64,11 @@ public enum ParseDialect {
/**
* Doris parser and it's version is 2.0.0.
*/
- DORIS_2_ALL("2.*");
+ DORIS_2_ALL("2.*"),
+ /**
+ * Spark sql parser and it's version is 3.x.
+ */
+ SPARK_SQL_3_ALL("3.*");
private final String version;
Version(String version) {
@@ -81,7 +91,11 @@ public enum ParseDialect {
/**
* Doris parser dialect
*/
- DORIS("doris");
+ DORIS("doris"),
+ /**
+ * Spark sql parser dialect
+ */
+ SPARK_SQL("spark_sql");
private String dialectName;
@@ -96,7 +110,7 @@ public enum ParseDialect {
/**
* Get dialect by name
*/
- public static Dialect getByName(String dialectName) {
+ public static @Nullable Dialect getByName(String dialectName) {
if (dialectName == null) {
return null;
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/spark/SparkSql3FnCallTransformers.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/spark/SparkSql3FnCallTransformers.java
new file mode 100644
index 00000000000..5a6ec21fc9a
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/spark/SparkSql3FnCallTransformers.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.doris.nereids.parser.spark;
+
+import org.apache.doris.nereids.analyzer.PlaceholderExpression;
+import org.apache.doris.nereids.parser.AbstractFnCallTransformer;
+import org.apache.doris.nereids.parser.AbstractFnCallTransformers;
+import org.apache.doris.nereids.trees.expressions.Expression;
+
+import com.google.common.collect.Lists;
+
+/**
+ * The builder and factory for spark-sql 3.x {@link AbstractFnCallTransformer},
+ * and supply transform facade ability.
+ */
+public class SparkSql3FnCallTransformers extends AbstractFnCallTransformers {
+
+ private SparkSql3FnCallTransformers() {
+ }
+
+ @Override
+ protected void registerTransformers() {
+ doRegister("get_json_object", 2, "json_extract",
+ Lists.newArrayList(
+ PlaceholderExpression.of(Expression.class, 1),
+ PlaceholderExpression.of(Expression.class, 2)), true);
+
+ doRegister("get_json_object", 2, "json_extract",
+ Lists.newArrayList(
+ PlaceholderExpression.of(Expression.class, 1),
+ PlaceholderExpression.of(Expression.class, 2)), false);
+
+ doRegister("split", 2, "split_by_string",
+ Lists.newArrayList(
+ PlaceholderExpression.of(Expression.class, 1),
+ PlaceholderExpression.of(Expression.class, 2)), true);
+ doRegister("split", 2, "split_by_string",
+ Lists.newArrayList(
+ PlaceholderExpression.of(Expression.class, 1),
+ PlaceholderExpression.of(Expression.class, 2)), false);
+ // TODO: add other function transformer
+ }
+
+ @Override
+ protected void registerComplexTransformers() {
+ // TODO: add other complex function transformer
+ }
+
+ static class SingletonHolder {
+ private static final SparkSql3FnCallTransformers INSTANCE = new
SparkSql3FnCallTransformers();
+ }
+
+ public static SparkSql3FnCallTransformers getSingleton() {
+ return SingletonHolder.INSTANCE;
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/spark/SparkSql3LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/spark/SparkSql3LogicalPlanBuilder.java
new file mode 100644
index 00000000000..d01f475871e
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/spark/SparkSql3LogicalPlanBuilder.java
@@ -0,0 +1,88 @@
+// 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.doris.nereids.parser.spark;
+
+import org.apache.doris.nereids.DorisParser;
+import org.apache.doris.nereids.analyzer.UnboundFunction;
+import org.apache.doris.nereids.exceptions.ParseException;
+import org.apache.doris.nereids.parser.LogicalPlanBuilder;
+import org.apache.doris.nereids.parser.ParseDialect;
+import org.apache.doris.nereids.parser.ParserContext;
+import org.apache.doris.nereids.parser.ParserUtils;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.Function;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSubQueryAlias;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Extends from {@link org.apache.doris.nereids.parser.LogicalPlanBuilder},
+ * just focus on the difference between these query syntax.
+ */
+public class SparkSql3LogicalPlanBuilder extends LogicalPlanBuilder {
+ // use a default alias name if not exists, keep the same name with
spark-sql
+ public static final String DEFAULT_TABLE_ALIAS =
"__auto_generated_subquery_name";
+
+ private final ParserContext parserContext;
+
+ public SparkSql3LogicalPlanBuilder() {
+ this.parserContext = new ParserContext(ParseDialect.SPARK_SQL_3_ALL);
+ }
+
+ @Override
+ public LogicalPlan visitAliasedQuery(DorisParser.AliasedQueryContext ctx) {
+ LogicalPlan plan = withTableAlias(visitQuery(ctx.query()),
ctx.tableAlias());
+ for (DorisParser.LateralViewContext lateralViewContext :
ctx.lateralView()) {
+ plan = withGenerate(plan, lateralViewContext);
+ }
+ return plan;
+ }
+
+ @Override
+ public Expression visitFunctionCall(DorisParser.FunctionCallContext ctx) {
+ Expression expression = super.visitFunctionCall(ctx);
+ if (!(expression instanceof UnboundFunction)) {
+ return expression;
+ }
+ UnboundFunction sourceFunction = (UnboundFunction) expression;
+ Function transformedFunction =
SparkSql3FnCallTransformers.getSingleton().transform(
+ sourceFunction.getName(),
+ sourceFunction.getArguments(),
+ this.parserContext
+ );
+ if (transformedFunction == null) {
+ return expression;
+ }
+ return transformedFunction;
+ }
+
+ private LogicalPlan withTableAlias(LogicalPlan plan,
DorisParser.TableAliasContext ctx) {
+ if (ctx.strictIdentifier() == null) {
+ return plan;
+ }
+ return ParserUtils.withOrigin(ctx.strictIdentifier(), () -> {
+ String alias =
StringUtils.isEmpty(ctx.strictIdentifier().getText())
+ ? DEFAULT_TABLE_ALIAS :
ctx.strictIdentifier().getText();
+ if (null != ctx.identifierList()) {
+ throw new ParseException("Do not implemented", ctx);
+ }
+ return new LogicalSubQueryAlias<>(alias, plan);
+ });
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/ComplexTrinoFnCallTransformer.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/ComplexTrinoFnCallTransformer.java
index e13229423c6..d3a687a289f 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/ComplexTrinoFnCallTransformer.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/ComplexTrinoFnCallTransformer.java
@@ -17,6 +17,8 @@
package org.apache.doris.nereids.parser.trino;
+import org.apache.doris.nereids.parser.AbstractFnCallTransformer;
+
/**
* Trino complex function transformer
*/
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformers.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformers.java
index a5e7f07fe17..883cb1cd132 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformers.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoFnCallTransformers.java
@@ -18,113 +18,45 @@
package org.apache.doris.nereids.parser.trino;
import org.apache.doris.nereids.analyzer.PlaceholderExpression;
-import org.apache.doris.nereids.analyzer.UnboundFunction;
-import org.apache.doris.nereids.parser.ParserContext;
+import org.apache.doris.nereids.parser.AbstractFnCallTransformer;
+import org.apache.doris.nereids.parser.AbstractFnCallTransformers;
import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.functions.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
-import java.util.List;
-import java.util.stream.Collectors;
-
/**
- * The builder and factory for {@link
org.apache.doris.nereids.parser.trino.TrinoFnCallTransformer},
+ * The builder and factory for trino {@link AbstractFnCallTransformer},
* and supply transform facade ability.
*/
-public class TrinoFnCallTransformers {
-
- private static ImmutableListMultimap<String, AbstractFnCallTransformer>
TRANSFORMER_MAP;
- private static ImmutableListMultimap<String, AbstractFnCallTransformer>
COMPLEX_TRANSFORMER_MAP;
- private static final ImmutableListMultimap.Builder<String,
AbstractFnCallTransformer> transformerBuilder =
- ImmutableListMultimap.builder();
- private static final ImmutableListMultimap.Builder<String,
AbstractFnCallTransformer> complexTransformerBuilder =
- ImmutableListMultimap.builder();
-
- static {
- registerTransformers();
- registerComplexTransformers();
- }
+public class TrinoFnCallTransformers extends AbstractFnCallTransformers {
private TrinoFnCallTransformers() {
}
- /**
- * Function transform facade
- */
- public static Function transform(String sourceFnName, List<Expression>
sourceFnTransformedArguments,
- ParserContext context) {
- List<AbstractFnCallTransformer> transformers =
getTransformers(sourceFnName);
- return doTransform(transformers, sourceFnName,
sourceFnTransformedArguments, context);
- }
-
- private static Function doTransform(List<AbstractFnCallTransformer>
transformers,
- String sourceFnName,
- List<Expression> sourceFnTransformedArguments,
- ParserContext context) {
- for (AbstractFnCallTransformer transformer : transformers) {
- if (transformer.check(sourceFnName, sourceFnTransformedArguments,
context)) {
- Function transformedFunction =
- transformer.transform(sourceFnName,
sourceFnTransformedArguments, context);
- if (transformedFunction == null) {
- continue;
- }
- return transformedFunction;
- }
- }
- return null;
- }
-
- private static List<AbstractFnCallTransformer> getTransformers(String
sourceFnName) {
- ImmutableList<AbstractFnCallTransformer> fnCallTransformers =
- TRANSFORMER_MAP.get(sourceFnName);
- ImmutableList<AbstractFnCallTransformer> complexFnCallTransformers =
- COMPLEX_TRANSFORMER_MAP.get(sourceFnName);
- return ImmutableList.copyOf(Iterables.concat(fnCallTransformers,
complexFnCallTransformers));
- }
-
- private static void registerTransformers() {
+ @Override
+ protected void registerTransformers() {
registerStringFunctionTransformer();
// TODO: add other function transformer
- // build transformer map in the end
- TRANSFORMER_MAP = transformerBuilder.build();
}
- private static void registerComplexTransformers() {
+ @Override
+ protected void registerComplexTransformers() {
DateDiffFnCallTransformer dateDiffFnCallTransformer = new
DateDiffFnCallTransformer();
doRegister(dateDiffFnCallTransformer.getSourceFnName(),
dateDiffFnCallTransformer);
// TODO: add other complex function transformer
- // build complex transformer map in the end
- COMPLEX_TRANSFORMER_MAP = complexTransformerBuilder.build();
}
- private static void registerStringFunctionTransformer() {
+ protected void registerStringFunctionTransformer() {
doRegister("codepoint", 1, "ascii",
Lists.newArrayList(PlaceholderExpression.of(Expression.class,
1)), false);
// TODO: add other string function transformer
}
- private static void doRegister(
- String sourceFnNme,
- int sourceFnArgumentsNum,
- String targetFnName,
- List<? extends Expression> targetFnArguments,
- boolean variableArgument) {
-
- List<Expression> castedTargetFnArguments = targetFnArguments
- .stream()
- .map(each -> (Expression) each)
- .collect(Collectors.toList());
- transformerBuilder.put(sourceFnNme, new TrinoFnCallTransformer(new
UnboundFunction(
- targetFnName, castedTargetFnArguments), variableArgument,
sourceFnArgumentsNum));
+ static class SingletonHolder {
+ private static final TrinoFnCallTransformers INSTANCE = new
TrinoFnCallTransformers();
}
- private static void doRegister(
- String sourceFnNme,
- AbstractFnCallTransformer transformer) {
- complexTransformerBuilder.put(sourceFnNme, transformer);
+ public static TrinoFnCallTransformers getSingleton() {
+ return SingletonHolder.INSTANCE;
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/LogicalPlanTrinoBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoLogicalPlanBuilder.java
similarity index 98%
rename from
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/LogicalPlanTrinoBuilder.java
rename to
fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoLogicalPlanBuilder.java
index be913200bc4..c1f9b9fbba8 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/LogicalPlanTrinoBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoLogicalPlanBuilder.java
@@ -55,7 +55,7 @@ import java.util.stream.Collectors;
* The actually planBuilder for Trino SQL to Doris logical plan.
* It depends on {@link io.trino.sql.tree.AstVisitor}
*/
-public class LogicalPlanTrinoBuilder extends
io.trino.sql.tree.AstVisitor<Object, ParserContext> {
+public class TrinoLogicalPlanBuilder extends
io.trino.sql.tree.AstVisitor<Object, ParserContext> {
public Object visit(io.trino.sql.tree.Node node, ParserContext context) {
return this.process(node, context);
@@ -145,7 +145,7 @@ public class LogicalPlanTrinoBuilder extends
io.trino.sql.tree.AstVisitor<Object
protected Function visitFunctionCall(io.trino.sql.tree.FunctionCall node,
ParserContext context) {
List<Expression> exprs = visit(node.getArguments(), context,
Expression.class);
Function transformedFn =
- TrinoFnCallTransformers.transform(node.getName().toString(),
exprs, context);
+
TrinoFnCallTransformers.getSingleton().transform(node.getName().toString(),
exprs, context);
if (transformedFn == null) {
transformedFn = new UnboundFunction(node.getName().toString(),
exprs);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoParser.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoParser.java
index b781bfc47ff..671af5e7f05 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoParser.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/trino/TrinoParser.java
@@ -17,16 +17,74 @@
package org.apache.doris.nereids.parser.trino;
+import org.apache.doris.analysis.StatementBase;
+import org.apache.doris.nereids.StatementContext;
+import org.apache.doris.nereids.exceptions.UnsupportedDialectException;
+import org.apache.doris.nereids.glue.LogicalPlanAdapter;
+import org.apache.doris.nereids.parser.ParseDialect;
+import org.apache.doris.nereids.parser.ParserContext;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.qe.SessionVariable;
+
+import com.google.common.base.Preconditions;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import javax.annotation.Nullable;
+
/**
* Trino Parser, depends on 395 trino-parser, and 4.9.3 antlr-runtime
*/
public class TrinoParser {
+
+ public static final Logger LOG = LogManager.getLogger(TrinoParser.class);
+
private static final io.trino.sql.parser.ParsingOptions PARSING_OPTIONS =
new io.trino.sql.parser.ParsingOptions(
io.trino.sql.parser.ParsingOptions.DecimalLiteralTreatment.AS_DECIMAL);
- public static io.trino.sql.tree.Statement parse(String query) {
+ /**
+ * Parse with trino syntax, return null if parse failed
+ */
+ public static @Nullable List<StatementBase> parse(String sql,
SessionVariable sessionVariable) {
+ final List<StatementBase> logicalPlans = new ArrayList<>();
+ try {
+ io.trino.sql.parser.StatementSplitter splitter = new
io.trino.sql.parser.StatementSplitter(sql);
+ ParserContext parserContext = new
ParserContext(ParseDialect.TRINO_395);
+ StatementContext statementContext = new StatementContext();
+ for (io.trino.sql.parser.StatementSplitter.Statement statement :
splitter.getCompleteStatements()) {
+ Object parsedPlan = parseSingle(statement.statement(),
parserContext);
+ logicalPlans.add(parsedPlan == null
+ ? null : new LogicalPlanAdapter((LogicalPlan)
parsedPlan, statementContext));
+ }
+ if (logicalPlans.isEmpty() ||
logicalPlans.stream().anyMatch(Objects::isNull)) {
+ return null;
+ }
+ return logicalPlans;
+ } catch (io.trino.sql.parser.ParsingException |
UnsupportedDialectException e) {
+ LOG.debug("Failed to parse logical plan from trino, sql is :{}",
sql, e);
+ return null;
+ }
+ }
+
+ private static io.trino.sql.tree.Statement parse(String sql) {
io.trino.sql.parser.SqlParser sqlParser = new
io.trino.sql.parser.SqlParser();
- return sqlParser.createStatement(query, PARSING_OPTIONS);
+ return sqlParser.createStatement(sql, PARSING_OPTIONS);
+ }
+
+ /**
+ * Parse trino dialect sql.
+ *
+ * @param sql sql string
+ * @param parserContext parse context
+ * @return logical plan
+ */
+ public static <T> T parseSingle(String sql, ParserContext parserContext) {
+ Preconditions.checkArgument(parserContext.getParserDialect() ==
ParseDialect.TRINO_395);
+ io.trino.sql.tree.Statement statement = TrinoParser.parse(sql);
+ return (T) new TrinoLogicalPlanBuilder().visit(statement,
parserContext);
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
index bf7a652fd9f..694a61b9f7e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
@@ -188,7 +188,7 @@ public abstract class ConnectProcessor {
// Nereids do not support prepare and execute now, so forbid prepare
command, only process query command
if (mysqlCommand == MysqlCommand.COM_QUERY &&
ctx.getSessionVariable().isEnableNereidsPlanner()) {
try {
- stmts = new NereidsParser().parseSQL(originStmt);
+ stmts = new NereidsParser().parseSQL(originStmt,
ctx.getSessionVariable());
} catch (NotSupportedException e) {
// Parse sql failed, audit it and return
handleQueryException(e, originStmt, null, null);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index 50af27f47f6..463b54771bb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -598,7 +598,7 @@ public class StmtExecutor {
}
List<StatementBase> statements;
try {
- statements = new NereidsParser().parseSQL(originStmt.originStmt);
+ statements = new NereidsParser().parseSQL(originStmt.originStmt,
context.getSessionVariable());
} catch (Exception e) {
throw new ParseException("Nereids parse failed. " +
e.getMessage());
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
index 28262799a57..9d0ca7176a4 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
@@ -187,7 +187,7 @@ public class NereidsParserTest extends ParserTestBase {
}
@Test
- public void testParseSQLWithDialect() {
+ public void testParseSQLWithTrinoDialect() {
String sql = "select `AD``D` from t1 where a = 1;explain graph select
`AD``D` from t1 where a = 1;";
NereidsParser nereidsParser = new NereidsParser();
SessionVariable sessionVariable = new SessionVariable();
@@ -203,6 +203,24 @@ public class NereidsParserTest extends ParserTestBase {
Assertions.assertTrue(logicalPlan1 instanceof ExplainCommand);
}
+ @Test
+ public void testParseSQLWithSparkSqlDialect() {
+ // doris parser will throw a ParseException when derived table does
not have alias
+ String sql1 = "select * from (select * from t1);";
+ NereidsParser nereidsParser = new NereidsParser();
+ Assertions.assertThrows(ParseException.class, () ->
nereidsParser.parseSQL(sql1),
+ "Every derived table must have its own alias");
+
+ // test parse with spark-sql dialect
+ SessionVariable sessionVariable = new SessionVariable();
+ sessionVariable.setSqlDialect("spark_sql");
+ List<StatementBase> statementBases = nereidsParser.parseSQL(sql1,
sessionVariable);
+ Assertions.assertEquals(1, statementBases.size());
+ Assertions.assertTrue(statementBases.get(0) instanceof
LogicalPlanAdapter);
+ LogicalPlan logicalPlan = ((LogicalPlanAdapter)
statementBases.get(0)).getLogicalPlan();
+ Assertions.assertTrue(logicalPlan instanceof UnboundResultSink);
+ }
+
@Test
public void testParseJoin() {
NereidsParser nereidsParser = new NereidsParser();
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/spark/FnTransformTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/spark/FnTransformTest.java
new file mode 100644
index 00000000000..f652e171280
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/spark/FnTransformTest.java
@@ -0,0 +1,55 @@
+// 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.doris.nereids.parser.spark;
+
+import org.apache.doris.nereids.parser.NereidsParser;
+import org.apache.doris.nereids.parser.ParserTestBase;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Spark SQL to Doris function mapping test.
+ */
+public class FnTransformTest extends ParserTestBase {
+
+ @Test
+ public void testCommonFnTransform() {
+ NereidsParser nereidsParser = new NereidsParser();
+
+ String sql1 = "SELECT json_extract('{\"a\": 1}', '$.a') as b FROM t";
+ String dialectSql1 = "SELECT get_json_object('{\"a\": 1}', '$.a') as b
FROM t";
+ LogicalPlan logicalPlan1 = nereidsParser.parseSingle(sql1);
+ LogicalPlan dialectLogicalPlan1 =
nereidsParser.parseSingle(dialectSql1,
+ new SparkSql3LogicalPlanBuilder());
+ Assertions.assertEquals(dialectLogicalPlan1, logicalPlan1);
+
Assertions.assertTrue(dialectLogicalPlan1.child(0).toString().toLowerCase()
+ .contains("json_extract('{\"a\": 1}', '$.a')"));
+
+ String sql2 = "SELECT json_extract(a, '$.a') as b FROM t";
+ String dialectSql2 = "SELECT get_json_object(a, '$.a') as b FROM t";
+ LogicalPlan logicalPlan2 = nereidsParser.parseSingle(sql2);
+ LogicalPlan dialectLogicalPlan2 =
nereidsParser.parseSingle(dialectSql2,
+ new SparkSql3LogicalPlanBuilder());
+ Assertions.assertEquals(dialectLogicalPlan2, logicalPlan2);
+
Assertions.assertTrue(dialectLogicalPlan2.child(0).toString().toLowerCase()
+ .contains("json_extract('a, '$.a')"));
+ }
+
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TrinoDialectPlanParseChecker.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TrinoDialectPlanParseChecker.java
index 0217eb7b8b6..9e22895cb79 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TrinoDialectPlanParseChecker.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TrinoDialectPlanParseChecker.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.util;
import org.apache.doris.nereids.parser.ParseDialect;
import org.apache.doris.nereids.parser.ParserContext;
+import org.apache.doris.nereids.parser.trino.TrinoParser;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import com.google.common.base.Supplier;
@@ -37,7 +38,7 @@ public class TrinoDialectPlanParseChecker extends
ParseChecker {
public TrinoDialectPlanParseChecker(String sql) {
super(sql);
this.parsedPlanSupplier =
- Suppliers.memoize(() -> PARSER.parseSingleWithDialect(sql, new
ParserContext(ParseDialect.TRINO_395)));
+ Suppliers.memoize(() -> TrinoParser.parseSingle(sql, new
ParserContext(ParseDialect.TRINO_395)));
}
public TrinoDialectPlanParseChecker assertEquals(LogicalPlan plan) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]