From caec862d91e314d2d941cf6415453ef439c65d1c Mon Sep 17 00:00:00 2001 From: morrySnow <101034200+morrySnow@users.noreply.github.com> Date: Mon, 22 Aug 2022 23:06:02 +0800 Subject: [PATCH] [feature](Nereids)add type coercion rule for nereids (#11802) - add an interface ExpectsInputTypes to Expression - add an interface ImplicitCastInputTypes to Expression - add a Expression rewrite rule for type coercion - add a Check Analysis Rule to check whether Plan is Semantically correct if Expression implements ImplicitCastInputTypes, type coercion rule will automatic rewrite its children that casting it to the most suitable type. If Expression implements ExpectsInputTypes, Check Analysis will check its children's type whether accepted by expects input types. --- .../nereids/analyzer/NereidsAnalyzer.java | 3 + .../doris/nereids/analyzer/UnboundAlias.java | 4 +- .../nereids/analyzer/UnboundFunction.java | 2 +- .../doris/nereids/analyzer/UnboundStar.java | 2 +- .../glue/translator/ExpressionTranslator.java | 74 +-- .../batch/CheckAnalysisJob.java} | 17 +- .../nereids/parser/LogicalPlanBuilder.java | 27 +- .../apache/doris/nereids/rules/RuleType.java | 5 + .../nereids/rules/analysis/CheckAnalysis.java | 49 ++ .../rewrite/ExpressionNormalization.java | 4 +- .../rewrite/rules/SimplifyCastRule.java | 6 +- .../rewrite/rules/TypeCoercion.java | 179 ++++++ .../logical/PushPredicateThroughJoin.java | 2 +- .../stats/FilterSelectivityCalculator.java | 4 +- .../doris/nereids/trees/expressions/Add.java | 26 +- .../nereids/trees/expressions/Alias.java | 3 +- .../doris/nereids/trees/expressions/And.java | 1 + .../nereids/trees/expressions/Arithmetic.java | 188 ------ .../nereids/trees/expressions/Between.java | 9 +- .../trees/expressions/BinaryArithmetic.java | 54 ++ .../trees/expressions/BinaryOperator.java | 75 +++ .../nereids/trees/expressions/CaseWhen.java | 44 +- .../doris/nereids/trees/expressions/Cast.java | 46 +- .../expressions/ComparisonPredicate.java | 38 +- .../trees/expressions/CompoundPredicate.java | 44 +- .../nereids/trees/expressions/Divide.java | 19 +- .../nereids/trees/expressions/Exists.java | 1 + .../nereids/trees/expressions/Expression.java | 35 ++ .../trees/expressions/InPredicate.java | 2 +- .../nereids/trees/expressions/InSubquery.java | 12 +- .../nereids/trees/expressions/ListQuery.java | 1 + .../doris/nereids/trees/expressions/Mod.java | 19 +- .../nereids/trees/expressions/Multiply.java | 18 +- .../doris/nereids/trees/expressions/Not.java | 20 +- .../trees/expressions/NullSafeEqual.java | 2 +- .../doris/nereids/trees/expressions/Or.java | 1 + .../trees/expressions/ScalarSubquery.java | 1 + .../doris/nereids/trees/expressions/Slot.java | 2 + .../trees/expressions/SlotReference.java | 2 +- .../expressions/StringRegexPredicate.java | 20 +- .../trees/expressions/SubqueryExpr.java | 2 +- .../nereids/trees/expressions/Subtract.java | 19 +- .../expressions/TimestampArithmetic.java | 25 +- .../nereids/trees/expressions/WhenClause.java | 28 +- .../trees/expressions/functions/Avg.java | 32 +- .../trees/expressions/functions/Min.java | 2 +- .../expressions/functions/Substring.java | 25 +- .../trees/expressions/functions/Sum.java | 25 +- .../trees/expressions/functions/Year.java | 22 +- .../{ => literal}/BigIntLiteral.java | 2 +- .../{ => literal}/BooleanLiteral.java | 2 +- .../expressions/literal/CharLiteral.java | 56 ++ .../{ => literal}/DateLiteral.java | 9 +- .../{ => literal}/DateTimeLiteral.java | 9 +- .../expressions/literal/DecimalLiteral.java | 53 ++ .../{ => literal}/DoubleLiteral.java | 2 +- .../expressions/literal/FloatLiteral.java | 51 ++ .../{ => literal}/IntegerLiteral.java | 2 +- .../{ => literal}/IntervalLiteral.java | 12 +- .../expressions/literal/LargeIntLiteral.java | 59 ++ .../expressions/{ => literal}/Literal.java | 4 +- .../{ => literal}/NullLiteral.java | 4 +- .../expressions/literal/SmallIntLiteral.java | 58 ++ .../{ => literal}/StringLiteral.java | 5 +- .../expressions/literal/TinyIntLiteral.java | 58 ++ .../expressions/literal/VarcharLiteral.java | 61 ++ .../{ => shape}/BinaryExpression.java | 3 +- .../{ => shape}/LeafExpression.java | 3 +- .../{ => shape}/TernaryExpression.java | 3 +- .../{ => shape}/UnaryExpression.java | 3 +- .../typecoercion/ExpectsInputTypes.java | 37 ++ .../typecoercion/ImplicitCastInputTypes.java} | 6 +- .../typecoercion/TypeCheckResult.java | 49 ++ .../visitor/DefaultSubExprRewriter.java | 5 +- .../visitor/ExpressionVisitor.java | 82 ++- .../trees/plans/logical/LogicalLimit.java | 2 +- .../trees/plans/physical/PhysicalLimit.java | 2 +- .../doris/nereids/types/BigIntType.java | 17 +- .../doris/nereids/types/BooleanType.java | 6 + .../apache/doris/nereids/types/CharType.java | 79 +++ .../apache/doris/nereids/types/DataType.java | 90 ++- .../doris/nereids/types/DateTimeType.java | 1 + .../apache/doris/nereids/types/DateType.java | 1 + .../doris/nereids/types/DecimalType.java | 118 +++- .../doris/nereids/types/DoubleType.java | 21 + .../apache/doris/nereids/types/FloatType.java | 53 ++ .../doris/nereids/types/IntegerType.java | 16 + .../doris/nereids/types/LargeIntType.java | 53 ++ .../apache/doris/nereids/types/NullType.java | 1 + .../doris/nereids/types/SmallIntType.java | 53 ++ .../doris/nereids/types/StringType.java | 23 +- .../doris/nereids/types/TinyIntType.java | 53 ++ .../doris/nereids/types/VarcharType.java | 28 +- .../AbstractDataType.java} | 23 +- .../nereids/types/coercion/AnyDataType.java | 43 ++ .../nereids/types/coercion/CharacterType.java | 55 ++ .../types/coercion/FractionalType.java | 43 ++ .../nereids/types/coercion/IntegralType.java | 44 ++ .../nereids/types/coercion/NumericType.java | 50 ++ .../types/{ => coercion}/PrimitiveType.java | 10 +- .../types/coercion/TypeCollection.java | 74 +++ .../doris/nereids/util/ExpressionUtils.java | 2 +- .../doris/nereids/util/TypeCoercionUtils.java | 278 +++++++++ .../statistics/SlotStatsDeriveResult.java | 2 +- .../apache/doris/nereids/memo/MemoTest.java | 2 +- .../rules/analysis/CheckAnalysisTest.java | 44 ++ .../rewrite/ExpressionRewriteTest.java | 6 +- .../expression/rewrite/TypeCoercionTest.java | 160 ++++++ .../logical/AggregateDisassembleTest.java | 2 +- .../rewrite/logical/JoinReorderTest.java | 2 +- .../logical/MergeConsecutiveFilterTest.java | 2 +- .../logical/MergeConsecutiveProjectsTest.java | 2 +- .../logical/PushDownPredicateTest.java | 31 +- ...shDownPredicateThroughAggregationTest.java | 2 +- .../nereids/stats/StatsCalculatorTest.java | 2 +- .../expressions/ExpectedInputTypesTest.java | 505 ++++++++++++++++ .../trees/expressions/ExpressionTest.java | 2 + .../trees/expressions/LiteralTest.java | 6 + .../nereids/trees/plans/PlanEqualsTest.java | 2 +- .../nereids/types/AbstractDataTypeTest.java | 540 ++++++++++++++++++ .../doris/nereids/types/DataTypeTest.java | 87 ++- .../doris/nereids/types/DecimalTypeTest.java | 47 ++ .../nereids/util/TypeCoercionUtilsTest.java | 199 +++++++ .../data/nereids_syntax_p0/inpredicate.out | 7 + .../nereids_syntax_p0/inpredicate.groovy | 15 +- 125 files changed, 4197 insertions(+), 588 deletions(-) rename fe/fe-core/src/main/java/org/apache/doris/nereids/{types/IntegralType.java => jobs/batch/CheckAnalysisJob.java} (61%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAnalysis.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java delete mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryArithmetic.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryOperator.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/BigIntLiteral.java (96%) rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/BooleanLiteral.java (96%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/CharLiteral.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/DateLiteral.java (93%) rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/DateTimeLiteral.java (94%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/DoubleLiteral.java (96%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteral.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/IntegerLiteral.java (96%) rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/IntervalLiteral.java (83%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/LargeIntLiteral.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/Literal.java (94%) rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/NullLiteral.java (92%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/SmallIntLiteral.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => literal}/StringLiteral.java (94%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TinyIntLiteral.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => shape}/BinaryExpression.java (89%) rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => shape}/LeafExpression.java (89%) rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => shape}/TernaryExpression.java (89%) rename fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/{ => shape}/UnaryExpression.java (89%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/ExpectsInputTypes.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/{types/FractionalType.java => trees/expressions/typecoercion/ImplicitCastInputTypes.java} (77%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/TypeCheckResult.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/CharType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/FloatType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/LargeIntType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/SmallIntType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/TinyIntType.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/types/{NumericType.java => coercion/AbstractDataType.java} (60%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/CharacterType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/FractionalType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/IntegralType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/NumericType.java rename fe/fe-core/src/main/java/org/apache/doris/nereids/types/{ => coercion}/PrimitiveType.java (79%) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/TypeCollection.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/TypeCoercionTest.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpectedInputTypesTest.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/types/DecimalTypeTest.java create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java index 98232b7e55..3b461e16a0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/NereidsAnalyzer.java @@ -19,6 +19,7 @@ package org.apache.doris.nereids.analyzer; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.jobs.batch.AnalyzeRulesJob; +import org.apache.doris.nereids.jobs.batch.CheckAnalysisJob; import org.apache.doris.nereids.jobs.batch.FinalizeAnalyzeJob; import org.apache.doris.nereids.rules.analysis.Scope; @@ -45,6 +46,8 @@ public class NereidsAnalyzer { public void analyze() { new AnalyzeRulesJob(cascadesContext, outerScope).execute(); new FinalizeAnalyzeJob(cascadesContext).execute(); + // check whether analyze result is meaningful + new CheckAnalysisJob(cascadesContext).execute(); } public CascadesContext getCascadesContext() { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java index 01c984d95d..e3c7f8a3e3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java @@ -20,7 +20,7 @@ package org.apache.doris.nereids.analyzer; import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; -import org.apache.doris.nereids.trees.expressions.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; @@ -53,7 +53,7 @@ public class UnboundAlias extends NamedExpression implements UnaryExpression, Un } @Override - public Expression withChildren(List children) { + public UnboundAlias withChildren(List children) { Preconditions.checkArgument(children.size() == 1); return new UnboundAlias(children.get(0)); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java index 73fb0403b4..66f07e5928 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java @@ -79,7 +79,7 @@ public class UnboundFunction extends Expression implements Unbound { } @Override - public Expression withChildren(List children) { + public UnboundFunction withChildren(List children) { return new UnboundFunction(name, isDistinct, isStar, children); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java index b20edb5367..75fc21c7d9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java @@ -18,8 +18,8 @@ package org.apache.doris.nereids.analyzer; import org.apache.doris.nereids.exceptions.UnboundException; -import org.apache.doris.nereids.trees.expressions.LeafExpression; import org.apache.doris.nereids.trees.expressions.NamedExpression; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.util.Utils; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java index e47dbebb87..5a40a8cb0b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java @@ -20,39 +20,27 @@ package org.apache.doris.nereids.glue.translator; import org.apache.doris.analysis.ArithmeticExpr; import org.apache.doris.analysis.BinaryPredicate; import org.apache.doris.analysis.BinaryPredicate.Operator; -import org.apache.doris.analysis.BoolLiteral; import org.apache.doris.analysis.CaseExpr; import org.apache.doris.analysis.CaseWhenClause; import org.apache.doris.analysis.CastExpr; import org.apache.doris.analysis.CompoundPredicate; import org.apache.doris.analysis.Expr; -import org.apache.doris.analysis.FloatLiteral; import org.apache.doris.analysis.FunctionCallExpr; import org.apache.doris.analysis.FunctionParams; -import org.apache.doris.analysis.IntLiteral; import org.apache.doris.analysis.LikePredicate; -import org.apache.doris.analysis.NullLiteral; -import org.apache.doris.analysis.StringLiteral; import org.apache.doris.analysis.TimestampArithmeticExpr; -import org.apache.doris.catalog.Type; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.And; -import org.apache.doris.nereids.trees.expressions.Arithmetic; import org.apache.doris.nereids.trees.expressions.Between; -import org.apache.doris.nereids.trees.expressions.BigIntLiteral; -import org.apache.doris.nereids.trees.expressions.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.BinaryArithmetic; import org.apache.doris.nereids.trees.expressions.CaseWhen; import org.apache.doris.nereids.trees.expressions.Cast; -import org.apache.doris.nereids.trees.expressions.DateLiteral; -import org.apache.doris.nereids.trees.expressions.DateTimeLiteral; -import org.apache.doris.nereids.trees.expressions.DoubleLiteral; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.GreaterThan; import org.apache.doris.nereids.trees.expressions.GreaterThanEqual; import org.apache.doris.nereids.trees.expressions.InPredicate; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.LessThan; import org.apache.doris.nereids.trees.expressions.LessThanEqual; import org.apache.doris.nereids.trees.expressions.Like; @@ -65,6 +53,7 @@ import org.apache.doris.nereids.trees.expressions.TimestampArithmetic; import org.apache.doris.nereids.trees.expressions.WhenClause; import org.apache.doris.nereids.trees.expressions.functions.BoundFunction; import org.apache.doris.nereids.trees.expressions.functions.Count; +import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor; import java.util.ArrayList; @@ -176,52 +165,8 @@ public class ExpressionTranslator extends DefaultExpressionVisitor { @Override public Expression visitCast(DorisParser.CastContext ctx) { return ParserUtils.withOrigin(ctx, () -> - new Cast(getExpression(ctx.expression()), ctx.identifier().getText())); + new Cast(getExpression(ctx.expression()), DataType.convertFromString(ctx.identifier().getText()))); } @Override @@ -586,9 +588,10 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor { BigInteger bigInt = new BigInteger(ctx.getText()); if (BigInteger.valueOf(bigInt.intValue()).equals(bigInt)) { return new IntegerLiteral(bigInt.intValue()); - } else { - // throw exception if out of long range + } else if (BigInteger.valueOf(bigInt.longValue()).equals(bigInt)) { return new BigIntLiteral(bigInt.longValueExact()); + } else { + return new LargeIntLiteral(bigInt); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java index f4cf4bd8ff..7f4c22ba71 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java @@ -40,6 +40,10 @@ public enum RuleType { RESOLVE_PROJECT_ALIAS(RuleTypeClass.REWRITE), RESOLVE_AGGREGATE_ALIAS(RuleTypeClass.REWRITE), PROJECT_TO_GLOBAL_AGGREGATE(RuleTypeClass.REWRITE), + + // check analysis rule + CHECK_ANALYSIS(RuleTypeClass.CHECK), + // rewrite rules AGGREGATE_DISASSEMBLE(RuleTypeClass.REWRITE), COLUMN_PRUNE_PROJECTION(RuleTypeClass.REWRITE), @@ -113,6 +117,7 @@ public enum RuleType { enum RuleTypeClass { REWRITE, EXPLORATION, + CHECK, IMPLEMENTATION, SENTINEL, ; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAnalysis.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAnalysis.java new file mode 100644 index 0000000000..4064294dd8 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAnalysis.java @@ -0,0 +1,49 @@ +// 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.rules.analysis; + +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.rules.Rule; +import org.apache.doris.nereids.rules.RuleType; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.typecoercion.TypeCheckResult; +import org.apache.doris.nereids.trees.plans.Plan; + +import java.util.Optional; + +/** + * Check analysis rule to check semantic correct after analysis by Nereids. + */ +public class CheckAnalysis extends OneAnalysisRuleFactory { + @Override + public Rule build() { + return any().then(this::checkExpressionInputTypes).toRule(RuleType.CHECK_ANALYSIS); + } + + private Plan checkExpressionInputTypes(Plan plan) { + final Optional firstFailed = plan.getExpressions().stream() + .map(Expression::checkInputDataTypes) + .filter(TypeCheckResult::failed) + .findFirst(); + + if (firstFailed.isPresent()) { + throw new AnalysisException(firstFailed.get().getMessage()); + } + return plan; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionNormalization.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionNormalization.java index 465e120085..33a1be951d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionNormalization.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionNormalization.java @@ -21,6 +21,7 @@ import org.apache.doris.nereids.rules.expression.rewrite.rules.BetweenToCompound import org.apache.doris.nereids.rules.expression.rewrite.rules.NormalizeBinaryPredicatesRule; import org.apache.doris.nereids.rules.expression.rewrite.rules.SimplifyCastRule; import org.apache.doris.nereids.rules.expression.rewrite.rules.SimplifyNotExprRule; +import org.apache.doris.nereids.rules.expression.rewrite.rules.TypeCoercion; import com.google.common.collect.ImmutableList; @@ -35,7 +36,8 @@ public class ExpressionNormalization extends ExpressionRewrite { NormalizeBinaryPredicatesRule.INSTANCE, BetweenToCompoundRule.INSTANCE, SimplifyNotExprRule.INSTANCE, - SimplifyCastRule.INSTANCE + SimplifyCastRule.INSTANCE, + TypeCoercion.INSTANCE ); private static final ExpressionRuleExecutor EXECUTOR = new ExpressionRuleExecutor(NORMALIZE_REWRITE_RULES); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyCastRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyCastRule.java index 81d6e685ca..7162b28fb3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyCastRule.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyCastRule.java @@ -40,7 +40,7 @@ public class SimplifyCastRule extends AbstractExpressionRewriteRule { } private Expression simplify(Cast cast) { - Expression source = cast.left(); + Expression source = cast.child(); // simplify inside if (source instanceof Cast) { source = simplify((Cast) source); @@ -52,8 +52,8 @@ public class SimplifyCastRule extends AbstractExpressionRewriteRule { return source; } - if (source != cast.left()) { - return new Cast(source, cast.right()); + if (source != cast.child()) { + return new Cast(source, cast.getDataType()); } return cast; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java new file mode 100644 index 0000000000..cc745e4b73 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java @@ -0,0 +1,179 @@ +// 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.rules.expression.rewrite.rules; + +import org.apache.doris.nereids.annotation.Developing; +import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule; +import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext; +import org.apache.doris.nereids.trees.expressions.BinaryOperator; +import org.apache.doris.nereids.trees.expressions.CaseWhen; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.InPredicate; +import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.util.TypeCoercionUtils; + +import com.google.common.collect.Lists; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * a rule to add implicit cast for expressions. + * This class is inspired by spark's TypeCoercion. + */ +@Developing +public class TypeCoercion extends AbstractExpressionRewriteRule { + + // TODO: + // 1. DecimalPrecision Process + // 2. Divide process + // 3. String promote with numeric in binary arithmetic + // 4. Date and DateTime process + + public static final TypeCoercion INSTANCE = new TypeCoercion(); + + @Override + public Expression visit(Expression expr, ExpressionRewriteContext ctx) { + if (expr instanceof ImplicitCastInputTypes) { + return visitImplicitCastInputTypes(expr, ctx); + } else { + return super.visit(expr, ctx); + } + } + + // TODO: add other expression visitor function to do type coercion if necessary. + + @Override + public Expression visitBinaryOperator(BinaryOperator binaryOperator, ExpressionRewriteContext context) { + Expression left = rewrite(binaryOperator.left(), context); + Expression right = rewrite(binaryOperator.right(), context); + + return Optional.of(TypeCoercionUtils.canHandleTypeCoercion(left.getDataType(), right.getDataType())) + .filter(Boolean::booleanValue) + .map(b -> TypeCoercionUtils.findTightestCommonType(left.getDataType(), right.getDataType())) + .filter(Optional::isPresent) + .map(Optional::get) + .filter(ct -> binaryOperator.inputType().acceptsType(ct)) + .filter(ct -> !left.getDataType().equals(ct) || !right.getDataType().equals(ct)) + .map(commonType -> { + Expression newLeft = TypeCoercionUtils.castIfNotSameType(left, commonType); + Expression newRight = TypeCoercionUtils.castIfNotSameType(right, commonType); + return binaryOperator.withChildren(newLeft, newRight); + }) + .orElse(binaryOperator.withChildren(left, right)); + } + + @Override + public Expression visitCaseWhen(CaseWhen caseWhen, ExpressionRewriteContext context) { + List rewrittenChildren = caseWhen.children().stream() + .map(e -> rewrite(e, context)).collect(Collectors.toList()); + CaseWhen newCaseWhen = caseWhen.withChildren(rewrittenChildren); + List dataTypesForCoercion = newCaseWhen.dataTypesForCoercion(); + if (dataTypesForCoercion.size() <= 1) { + return newCaseWhen; + } + DataType first = dataTypesForCoercion.get(0); + if (dataTypesForCoercion.stream().allMatch(dataType -> dataType.equals(first))) { + return newCaseWhen; + } + Optional optionalCommonType = TypeCoercionUtils.findWiderCommonType(dataTypesForCoercion); + return optionalCommonType + .map(commonType -> { + List newChildren + = newCaseWhen.getWhenClauses().stream() + .map(wc -> wc.withChildren(wc.getOperand(), + TypeCoercionUtils.castIfNotSameType(wc.getResult(), commonType))) + .collect(Collectors.toList()); + newCaseWhen.getDefaultValue() + .map(dv -> TypeCoercionUtils.castIfNotSameType(dv, commonType)) + .ifPresent(newChildren::add); + return newCaseWhen.withChildren(newChildren); + }) + .orElse(newCaseWhen); + } + + @Override + public Expression visitInPredicate(InPredicate inPredicate, ExpressionRewriteContext context) { + List rewrittenChildren = inPredicate.children().stream() + .map(e -> rewrite(e, context)).collect(Collectors.toList()); + InPredicate newInPredicate = inPredicate.withChildren(rewrittenChildren); + + if (newInPredicate.getOptions().stream().map(Expression::getDataType) + .allMatch(dt -> dt.equals(newInPredicate.getCompareExpr().getDataType()))) { + return newInPredicate; + } + Optional optionalCommonType = TypeCoercionUtils.findWiderCommonType(newInPredicate.children() + .stream().map(Expression::getDataType).collect(Collectors.toList())); + + return optionalCommonType + .map(commonType -> { + List newChildren = newInPredicate.children().stream() + .map(e -> TypeCoercionUtils.castIfNotSameType(e, commonType)) + .collect(Collectors.toList()); + return newInPredicate.withChildren(newChildren); + }) + .orElse(newInPredicate); + } + + /** + * Do implicit cast for expression's children. + */ + private Expression visitImplicitCastInputTypes(Expression expr, ExpressionRewriteContext ctx) { + ImplicitCastInputTypes implicitCastInputTypes = (ImplicitCastInputTypes) expr; + List newChildren = Lists.newArrayListWithCapacity(expr.arity()); + AtomicInteger changed = new AtomicInteger(0); + for (int i = 0; i < implicitCastInputTypes.expectedInputTypes().size(); i++) { + newChildren.add(implicitCast(expr.child(i), implicitCastInputTypes.expectedInputTypes().get(i), ctx) + .map(e -> { + changed.incrementAndGet(); + return e; + }) + .orElse(expr.child(0)) + ); + } + if (changed.get() != 0) { + return expr.withChildren(newChildren); + } else { + return expr; + } + } + + /** + * Return Optional.empty() if we cannot do or do not need to do implicit cast. + */ + @Developing + private Optional implicitCast(Expression input, AbstractDataType expected, + ExpressionRewriteContext ctx) { + Expression rewrittenInput = rewrite(input, ctx); + Optional castDataType = TypeCoercionUtils.implicitCast(rewrittenInput.getDataType(), expected); + if (castDataType.isPresent() && !castDataType.get().equals(rewrittenInput.getDataType())) { + return Optional.of(new Cast(rewrittenInput, castDataType.get())); + } else { + if (rewrittenInput == input) { + return Optional.empty(); + } else { + return Optional.of(rewrittenInput); + } + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java index bdb3961084..9f407be3c7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java @@ -20,10 +20,10 @@ package org.apache.doris.nereids.rules.rewrite.logical; import org.apache.doris.nereids.rules.Rule; import org.apache.doris.nereids.rules.RuleType; import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory; -import org.apache.doris.nereids.trees.expressions.BooleanLiteral; import org.apache.doris.nereids.trees.expressions.ComparisonPredicate; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterSelectivityCalculator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterSelectivityCalculator.java index 0953b8ffbb..09c2cd46e5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterSelectivityCalculator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/FilterSelectivityCalculator.java @@ -21,10 +21,10 @@ import org.apache.doris.nereids.trees.expressions.ComparisonPredicate; import org.apache.doris.nereids.trees.expressions.CompoundPredicate; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.Literal; import org.apache.doris.nereids.trees.expressions.Or; import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.statistics.ColumnStats; @@ -37,7 +37,7 @@ import java.util.Map; */ public class FilterSelectivityCalculator extends ExpressionVisitor { - private static double DEFAULT_SELECTIVITY = 0.1; + private static final double DEFAULT_SELECTIVITY = 0.1; private final Map slotRefToStats; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java index 503ec9ed4d..34e807a1c5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java @@ -17,8 +17,10 @@ package org.apache.doris.nereids.trees.expressions; -import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.analysis.ArithmeticExpr.Operator; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.NumericType; import com.google.common.base.Preconditions; @@ -27,15 +29,10 @@ import java.util.List; /** * Add Expression. */ -public class Add extends Arithmetic implements BinaryExpression { - public Add(Expression left, Expression right) { - super(ArithmeticOperator.ADD, left, right); - } +public class Add extends BinaryArithmetic { - @Override - public String toSql() { - return left().toSql() + ' ' + getArithmeticOperator().toString() - + ' ' + right().toSql(); + public Add(Expression left, Expression right) { + super(left, right, Operator.ADD); } @Override @@ -44,18 +41,13 @@ public class Add extends Arithmetic implements BinaryExpression { return new Add(children.get(0), children.get(1)); } - @Override - public boolean nullable() throws UnboundException { - return left().nullable() || right().nullable(); - } - @Override public R accept(ExpressionVisitor visitor, C context) { return visitor.visitAdd(this, context); } - - public String toString() { - return left().toString() + ' ' + getArithmeticOperator().toString() + ' ' + right().toString(); + @Override + public AbstractDataType inputType() { + return NumericType.INSTANCE; } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java index 453f082fad..a84955b287 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; @@ -123,7 +124,7 @@ public class Alias extends NamedExpression implements UnaryExpression { } @Override - public Expression withChildren(List children) { + public Alias withChildren(List children) { Preconditions.checkArgument(children.size() == 1); return new Alias(exprId, children.get(0), name); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java index 0991c7f663..966f8d5f74 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java @@ -27,6 +27,7 @@ import java.util.List; * And predicate expression. */ public class And extends CompoundPredicate { + /** * Desc: Constructor for CompoundPredicate. * diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java deleted file mode 100644 index 80dd2bf57e..0000000000 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java +++ /dev/null @@ -1,188 +0,0 @@ -// 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.trees.expressions; - - -import org.apache.doris.analysis.ArithmeticExpr; -import org.apache.doris.analysis.ArithmeticExpr.Operator; -import org.apache.doris.nereids.exceptions.UnboundException; -import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; -import org.apache.doris.nereids.types.DataType; - -import java.util.Objects; -import java.util.function.Function; - -/** - * All arithmetic operator. - */ -public abstract class Arithmetic extends Expression { - - enum OperatorPosition { - BINARY_INFIX, - UNARY_PREFIX, - UNARY_POSTFIX, - } - - /** - * All counts as expressions. - */ - @SuppressWarnings("checkstyle:RegexpSingleline") - public enum ArithmeticOperator { - MULTIPLY("*", "multiply", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.MULTIPLY), - DIVIDE("/", "divide", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.DIVIDE), - MOD("%", "mod", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.MOD), - ADD("+", "add", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.ADD), - SUBTRACT("-", "subtract", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.SUBTRACT), - //TODO: The following functions will be added later. - BITAND("&", "bitand", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.BITAND), - BITOR("|", "bitor", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.BITOR), - BITXOR("^", "bitxor", - Arithmetic.OperatorPosition.BINARY_INFIX, Operator.BITXOR), - BITNOT("~", "bitnot", - Arithmetic.OperatorPosition.UNARY_PREFIX, Operator.BITNOT); - - private final String description; - private final String name; - private final Arithmetic.OperatorPosition pos; - private final ArithmeticExpr.Operator staleOp; - - ArithmeticOperator(String description, - String name, - Arithmetic.OperatorPosition pos, - ArithmeticExpr.Operator staleOp) { - this.description = description; - this.name = name; - this.pos = pos; - this.staleOp = staleOp; - } - - @Override - public String toString() { - return description; - } - - public String getName() { - return name; - } - - public Arithmetic.OperatorPosition getPos() { - return pos; - } - - public Operator getStaleOp() { - return staleOp; - } - - public boolean isUnary() { - return pos == Arithmetic.OperatorPosition.UNARY_PREFIX - || pos == Arithmetic.OperatorPosition.UNARY_POSTFIX; - } - - public boolean isBinary() { - return pos == Arithmetic.OperatorPosition.BINARY_INFIX; - } - } - - private final ArithmeticOperator op; - - public Arithmetic(ArithmeticOperator op, Expression... children) { - super(children); - this.op = op; - } - - public ArithmeticOperator getArithmeticOperator() { - return op; - } - - @Override - public DataType getDataType() { - // TODO: split Unary and Binary arithmetic - int arity = arity(); - if (arity == 1) { - return child(0).getDataType(); - } else if (arity == 2) { - // TODO: binary arithmetic - return child(0).getDataType(); - } else { - return super.getDataType(); - } - } - - @Override - public boolean nullable() throws UnboundException { - if (op.isUnary()) { - return child(0).nullable(); - } else { - return child(0).nullable() || child(1).nullable(); - } - } - - public R accept(ExpressionVisitor visitor, C context) { - return visitor.visitArithmetic(this, context); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - Arithmetic that = (Arithmetic) o; - return op == that.op && Objects.equals(this.children(), that.children()); - } - - @Override - public int hashCode() { - return Objects.hash(op, children()); - } - - @Override - public String toString() { - return stringBuilder(Object::toString); - } - - @Override - public String toSql() { - return stringBuilder(Expression::toSql); - } - - private String stringBuilder(Function stringMapper) { - switch (op.getPos()) { - case BINARY_INFIX: - return stringMapper.apply(children.get(0)) + " " + op + " " + stringMapper.apply(children.get(1)); - case UNARY_PREFIX: - return op + stringMapper.apply(children.get(0)); - case UNARY_POSTFIX: - return stringMapper.apply(children.get(0)) + op; - default: - throw new IllegalStateException("Not supported operator: " + op); - } - } -} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java index 64b44580fb..2d3942b217 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.BooleanType; import org.apache.doris.nereids.types.DataType; @@ -32,9 +33,9 @@ import java.util.Objects; */ public class Between extends Expression implements TernaryExpression { - private Expression compareExpr; - private Expression lowerBound; - private Expression upperBound; + private final Expression compareExpr; + private final Expression lowerBound; + private final Expression upperBound; /** * Constructor of ComparisonPredicate. * @@ -88,7 +89,7 @@ public class Between extends Expression implements TernaryExpression { } @Override - public Expression withChildren(List children) { + public Between withChildren(List children) { Preconditions.checkArgument(children.size() == 3); return new Between(children.get(0), children.get(1), children.get(2)); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryArithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryArithmetic.java new file mode 100644 index 0000000000..fc453f988d --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryArithmetic.java @@ -0,0 +1,54 @@ +// 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.trees.expressions; + +import org.apache.doris.analysis.ArithmeticExpr.Operator; +import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DataType; + +/** + * binary arithmetic operator. Such as +, -, *, /. + */ +public abstract class BinaryArithmetic extends BinaryOperator { + + private final Operator legacyOperator; + + public BinaryArithmetic(Expression left, Expression right, Operator legacyOperator) { + super(left, right, legacyOperator.toString()); + this.legacyOperator = legacyOperator; + } + + public Operator getLegacyOperator() { + return legacyOperator; + } + + @Override + public DataType getDataType() throws UnboundException { + return left().getDataType(); + } + + @Override + public boolean nullable() throws UnboundException { + return child(0).nullable() || child(1).nullable(); + } + + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitBinaryArithmetic(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryOperator.java new file mode 100644 index 0000000000..d539cfa195 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryOperator.java @@ -0,0 +1,75 @@ +// 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.trees.expressions; + +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ExpectsInputTypes; +import org.apache.doris.nereids.types.coercion.AbstractDataType; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; + +/** + * Abstract for all binary operator, include binary arithmetic, compound predicate, comparison predicate. + */ +public abstract class BinaryOperator extends Expression implements BinaryExpression, ExpectsInputTypes { + + protected final String symbol; + + public BinaryOperator(Expression left, Expression right, String symbol) { + super(left, right); + this.symbol = symbol; + } + + public abstract AbstractDataType inputType(); + + @Override + public List expectedInputTypes() { + return ImmutableList.of(inputType(), inputType()); + } + + @Override + public String toSql() { + return "(" + left().toSql() + " " + symbol + " " + right().toSql() + ")"; + } + + @Override + public String toString() { + return "(" + left().toString() + " " + symbol + " " + right().toString() + ")"; + } + + @Override + public int hashCode() { + return Objects.hash(symbol, left(), right()); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BinaryOperator other = (BinaryOperator) o; + return Objects.equals(left(), other.left()) && Objects.equals(right(), other.right()); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CaseWhen.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CaseWhen.java index 0c5f8090cb..deed64941b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CaseWhen.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CaseWhen.java @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -37,35 +38,34 @@ import java.util.stream.Collectors; * If an else expr is given then it is the last child. */ public class CaseWhen extends Expression { - /** - * If default value exists, then defaultValueIndex is the index of the last element in children, - * otherwise it is -1 - */ - private final int defaultValueIndex; + + private final List whenClauses; + private final Optional defaultValue; public CaseWhen(List whenClauses) { super(whenClauses.toArray(new Expression[0])); - defaultValueIndex = -1; + this.whenClauses = ImmutableList.copyOf(Objects.requireNonNull(whenClauses)); + defaultValue = Optional.empty(); } public CaseWhen(List whenClauses, Expression defaultValue) { - super(ImmutableList.builder().addAll(whenClauses).add(defaultValue).build().toArray(new Expression[0])); - defaultValueIndex = children().size() - 1; + super(ImmutableList.builder() + .addAll(whenClauses).add(defaultValue).build() + .toArray(new Expression[0])); + this.whenClauses = ImmutableList.copyOf(Objects.requireNonNull(whenClauses)); + this.defaultValue = Optional.of(Objects.requireNonNull(defaultValue)); } public List getWhenClauses() { - List whenClauses = children().stream() - .filter(e -> e instanceof WhenClause) - .map(e -> (WhenClause) e) - .collect(Collectors.toList()); return whenClauses; } public Optional getDefaultValue() { - if (defaultValueIndex == -1) { - return Optional.empty(); - } - return Optional.of(child(defaultValueIndex)); + return defaultValue; + } + + public List dataTypesForCoercion() { + return whenClauses.stream().map(WhenClause::getDataType).collect(Collectors.toList()); } public R accept(ExpressionVisitor visitor, C context) { @@ -79,12 +79,12 @@ public class CaseWhen extends Expression { @Override public boolean nullable() { - for (Expression child : children()) { - if (child.nullable()) { + for (WhenClause whenClause : whenClauses) { + if (whenClause.nullable()) { return true; } } - return false; + return defaultValue.map(Expression::nullable).orElse(true); } @Override @@ -99,7 +99,7 @@ public class CaseWhen extends Expression { if (child instanceof WhenClause) { output.append(child.toSql()); } else { - output.append(" ELSE " + child.toSql()); + output.append(" ELSE ").append(child.toSql()); } } output.append(" END"); @@ -107,7 +107,7 @@ public class CaseWhen extends Expression { } @Override - public Expression withChildren(List children) { + public CaseWhen withChildren(List children) { Preconditions.checkArgument(children.size() >= 1); List whenClauseList = new ArrayList<>(); Expression defaultValue = null; @@ -117,7 +117,7 @@ public class CaseWhen extends Expression { } else if (children.size() - 1 == i) { defaultValue = children.get(i); } else { - throw new IllegalArgumentException("The children format needs to be [WhenClause*, DefaultValue+]"); + throw new IllegalArgumentException("The children format needs to be [WhenClause+, DefaultValue?]"); } } if (defaultValue == null) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java index 0e9839243a..321a334e39 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java @@ -18,35 +18,30 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; import com.google.common.base.Preconditions; import java.util.List; +import java.util.Objects; /** * cast function. */ -public class Cast extends Expression implements BinaryExpression { +public class Cast extends Expression implements UnaryExpression { - public Cast(Expression child, String type) { - super(child, new StringLiteral(type)); - } + private final DataType targetType; - public Cast(Expression child, StringLiteral type) { - super(child, type); - } - - @Override - public StringLiteral right() { - return (StringLiteral) BinaryExpression.super.right(); + public Cast(Expression child, DataType targetType) { + super(child); + this.targetType = targetType; } @Override public DataType getDataType() { - StringLiteral type = right(); - return DataType.convertFromString(type.getValue()); + return targetType; } @Override @@ -56,23 +51,36 @@ public class Cast extends Expression implements BinaryExpression { @Override public boolean nullable() { - return left().nullable(); + return child().nullable(); } @Override - public Expression withChildren(List children) { - Preconditions.checkArgument(children.size() == 2); - Preconditions.checkArgument(children.get(1) instanceof StringLiteral); - return new Cast(children.get(0), ((StringLiteral) children.get(1)).getValue()); + public Cast withChildren(List children) { + Preconditions.checkArgument(children.size() == 1); + return new Cast(children.get(0), getDataType()); } @Override public String toSql() throws UnboundException { - return "CAST(" + left().toSql() + " AS " + right().getValue() + ")"; + return "CAST(" + child().toSql() + " AS " + targetType + ")"; } @Override public String toString() { return toSql(); } + + @Override + public boolean equals(Object o) { + if (!super.equals(o)) { + return false; + } + Cast cast = (Cast) o; + return Objects.equals(targetType, cast.targetType); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), targetType); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java index 2b2471bf23..e442355d56 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java @@ -21,16 +21,14 @@ import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.BooleanType; import org.apache.doris.nereids.types.DataType; - -import java.util.Objects; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.AnyDataType; /** * Comparison predicate expression. * Such as: "=", "<", "<=", ">", ">=", "<=>" */ -public abstract class ComparisonPredicate extends Expression implements BinaryExpression { - - protected final String symbol; +public abstract class ComparisonPredicate extends BinaryOperator { /** * Constructor of ComparisonPredicate. @@ -39,8 +37,7 @@ public abstract class ComparisonPredicate extends Expression implements BinaryEx * @param right right child of comparison predicate */ public ComparisonPredicate(Expression left, Expression right, String symbol) { - super(left, right); - this.symbol = symbol; + super(left, right, symbol); } @Override @@ -48,36 +45,13 @@ public abstract class ComparisonPredicate extends Expression implements BinaryEx return BooleanType.INSTANCE; } - @Override - public boolean nullable() throws UnboundException { - return left().nullable() || right().nullable(); - } - - @Override - public String toSql() { - return "(" + left().toSql() + ' ' + symbol + ' ' + right().toSql() + ")"; - } - public R accept(ExpressionVisitor visitor, C context) { return visitor.visitComparisonPredicate(this, context); } @Override - public int hashCode() { - return Objects.hash(symbol, left(), right()); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ComparisonPredicate other = (ComparisonPredicate) o; - return Objects.equals(left(), other.left()) - && Objects.equals(right(), other.right()); + public AbstractDataType inputType() { + return AnyDataType.INSTANCE; } /** diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java index ed0973fb34..ac6834e4fd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java @@ -19,30 +19,25 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; - -import java.util.Objects; +import org.apache.doris.nereids.types.BooleanType; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; /** * Compound predicate expression. * Such as &&,||,AND,OR. */ -public abstract class CompoundPredicate extends Expression implements BinaryExpression { - protected final String symbol; +public abstract class CompoundPredicate extends BinaryOperator { /** * Desc: Constructor for CompoundPredicate. * * @param left left child of comparison predicate * @param right right child of comparison predicate + * @param symbol symbol used in sql */ public CompoundPredicate(Expression left, Expression right, String symbol) { - super(left, right); - this.symbol = symbol; - } - - @Override - public String toSql() { - return "(" + left().toSql() + " " + symbol + " " + right().toSql() + ")"; + super(left, right, symbol); } @Override @@ -50,32 +45,19 @@ public abstract class CompoundPredicate extends Expression implements BinaryExpr return left().nullable() || right().nullable(); } + @Override + public DataType getDataType() throws UnboundException { + return BooleanType.INSTANCE; + } + @Override public R accept(ExpressionVisitor visitor, C context) { return visitor.visitCompoundPredicate(this, context); } @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - CompoundPredicate other = (CompoundPredicate) o; - return Objects.equals(left(), other.left()) - && Objects.equals(right(), other.right()); - } - - @Override - public int hashCode() { - return Objects.hash(symbol, left(), right()); - } - - @Override - public String toString() { - return "(" + left().toString() + " " + symbol + " " + right().toString() + ")"; + public AbstractDataType inputType() { + return BooleanType.INSTANCE; } /** diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java index 25eba66388..9f4d676214 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java @@ -17,7 +17,10 @@ package org.apache.doris.nereids.trees.expressions; +import org.apache.doris.analysis.ArithmeticExpr.Operator; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.NumericType; import com.google.common.base.Preconditions; @@ -26,15 +29,10 @@ import java.util.List; /** * Divide Expression. */ -public class Divide extends Arithmetic implements BinaryExpression { - public Divide(Expression left, Expression right) { - super(ArithmeticOperator.DIVIDE, left, right); - } +public class Divide extends BinaryArithmetic { - @Override - public String toSql() { - return left().toSql() + ' ' + getArithmeticOperator().toString() - + ' ' + right().toSql(); + public Divide(Expression left, Expression right) { + super(left, right, Operator.DIVIDE); } @Override @@ -47,4 +45,9 @@ public class Divide extends Arithmetic implements BinaryExpression { public R accept(ExpressionVisitor visitor, C context) { return visitor.visitDivide(this, context); } + + @Override + public AbstractDataType inputType() { + return NumericType.INSTANCE; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java index 68b7ef3429..121da5beb7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Exists.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; import org.apache.doris.nereids.types.BooleanType; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java index 34b17cc275..ba68f8fca3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java @@ -20,10 +20,18 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.trees.AbstractTreeNode; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ExpectsInputTypes; +import org.apache.doris.nereids.trees.expressions.typecoercion.TypeCheckResult; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,6 +43,8 @@ import java.util.Objects; */ public abstract class Expression extends AbstractTreeNode { + private static final String INPUT_CHECK_ERROR_MESSAGE = "argument %d requires %s type, however '%s' is of %s type"; + private final Logger logger = LoggerFactory.getLogger(this.getClass()); public Expression(Expression... children) { @@ -53,6 +63,31 @@ public abstract class Expression extends AbstractTreeNode { throw new UnboundException("nullable"); } + public TypeCheckResult checkInputDataTypes() { + if (this instanceof ExpectsInputTypes) { + ExpectsInputTypes expectsInputTypes = (ExpectsInputTypes) this; + return checkInputDataTypes(children, expectsInputTypes.expectedInputTypes()); + } + return TypeCheckResult.SUCCESS; + } + + private TypeCheckResult checkInputDataTypes(List inputs, List inputTypes) { + Preconditions.checkArgument(inputs.size() == inputTypes.size()); + List errorMessages = Lists.newArrayList(); + for (int i = 0; i < inputs.size(); i++) { + Expression input = inputs.get(i); + AbstractDataType inputType = inputTypes.get(i); + if (!inputType.acceptsType(input.getDataType())) { + errorMessages.add(String.format(INPUT_CHECK_ERROR_MESSAGE, + i + 1, inputType.simpleString(), input.toSql(), input.getDataType().simpleString())); + } + } + if (!errorMessages.isEmpty()) { + return new TypeCheckResult(false, StringUtils.join(errorMessages, ", ")); + } + return TypeCheckResult.SUCCESS; + } + public R accept(ExpressionVisitor visitor, C context) { return visitor.visit(this, context); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java index 2a52c98ed0..a3904b2367 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java @@ -59,7 +59,7 @@ public class InPredicate extends Expression { } @Override - public Expression withChildren(List children) { + public InPredicate withChildren(List children) { Preconditions.checkArgument(children.size() > 1); return new InPredicate(children.get(0), ImmutableList.copyOf(children).subList(1, children.size())); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java index e020e22a4c..39c655d66a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InSubquery.java @@ -29,13 +29,14 @@ import java.util.Objects; /** * In predicate expression. */ -public class InSubquery extends SubqueryExpr implements BinaryExpression { - private Expression compareExpr; - private ListQuery listQuery; +public class InSubquery extends SubqueryExpr { + + private final Expression compareExpr; + private final ListQuery listQuery; public InSubquery(Expression compareExpression, ListQuery listQuery) { super(Objects.requireNonNull(listQuery.getQueryPlan(), "subquery can not be null")); - this.compareExpr = compareExpression; + this.compareExpr = Objects.requireNonNull(compareExpression); this.listQuery = listQuery; } @@ -68,9 +69,8 @@ public class InSubquery extends SubqueryExpr implements BinaryExpression { } @Override - public Expression withChildren(List children) { + public InSubquery withChildren(List children) { Preconditions.checkArgument(children.size() == 2); - Preconditions.checkArgument(children.get(0) instanceof Expression); Preconditions.checkArgument(children.get(1) instanceof ListQuery); return new InSubquery(children.get(0), (ListQuery) children.get(1)); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java index 3fb3224183..023195bcb2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ListQuery.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; import org.apache.doris.nereids.types.DataType; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java index 02eac22d39..175ff9969d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java @@ -17,7 +17,10 @@ package org.apache.doris.nereids.trees.expressions; +import org.apache.doris.analysis.ArithmeticExpr.Operator; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.NumericType; import com.google.common.base.Preconditions; @@ -26,15 +29,10 @@ import java.util.List; /** * Mod Expression. */ -public class Mod extends Arithmetic implements BinaryExpression { - public Mod(Expression left, Expression right) { - super(ArithmeticOperator.MOD, left, right); - } +public class Mod extends BinaryArithmetic { - @Override - public String toSql() { - return left().toSql() + ' ' + getArithmeticOperator().toString() - + ' ' + right().toSql(); + public Mod(Expression left, Expression right) { + super(left, right, Operator.MOD); } @Override @@ -47,4 +45,9 @@ public class Mod extends Arithmetic implements BinaryExpression { public R accept(ExpressionVisitor visitor, C context) { return visitor.visitMod(this, context); } + + @Override + public AbstractDataType inputType() { + return NumericType.INSTANCE; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java index a19386231b..3f2540d0ef 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java @@ -17,7 +17,10 @@ package org.apache.doris.nereids.trees.expressions; +import org.apache.doris.analysis.ArithmeticExpr.Operator; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.NumericType; import com.google.common.base.Preconditions; @@ -26,16 +29,16 @@ import java.util.List; /** * Multiply Expression. */ -public class Multiply extends Arithmetic implements BinaryExpression { +public class Multiply extends BinaryArithmetic { public Multiply(Expression left, Expression right) { - super(ArithmeticOperator.MULTIPLY, left, right); + super(left, right, Operator.MULTIPLY); } @Override - public String toSql() { - return left().toSql() + ' ' + getArithmeticOperator().toString() - + ' ' + right().toSql(); + public Expression withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new Multiply(children.get(0), children.get(1)); } @Override @@ -44,8 +47,7 @@ public class Multiply extends Arithmetic implements BinaryExpression { } @Override - public Expression withChildren(List children) { - Preconditions.checkArgument(children.size() == 2); - return new Multiply(children.get(0), children.get(1)); + public AbstractDataType inputType() { + return NumericType.INSTANCE; } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java index 80cef67b33..b1a2eee13b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java @@ -18,9 +18,15 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ExpectsInputTypes; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.BooleanType; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Objects; @@ -28,7 +34,9 @@ import java.util.Objects; /** * Not expression: not a. */ -public class Not extends Expression implements UnaryExpression { +public class Not extends Expression implements UnaryExpression, ExpectsInputTypes { + + public static final List EXPECTS_INPUT_TYPES = ImmutableList.of(BooleanType.INSTANCE); public Not(Expression child) { super(child); @@ -39,6 +47,11 @@ public class Not extends Expression implements UnaryExpression { return child().nullable(); } + @Override + public DataType getDataType() throws UnboundException { + return child().getDataType(); + } + @Override public R accept(ExpressionVisitor visitor, C context) { return visitor.visitNot(this, context); @@ -76,4 +89,9 @@ public class Not extends Expression implements UnaryExpression { Preconditions.checkArgument(children.size() == 1); return new Not(children.get(0)); } + + @Override + public List expectedInputTypes() { + return EXPECTS_INPUT_TYPES; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java index 8f736101c3..dc2ecbed42 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java @@ -55,7 +55,7 @@ public class NullSafeEqual extends ComparisonPredicate { } @Override - public Expression withChildren(List children) { + public NullSafeEqual withChildren(List children) { Preconditions.checkArgument(children.size() == 2); return new NullSafeEqual(children.get(0), children.get(1)); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java index 52ff529891..f9385f59c7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java @@ -27,6 +27,7 @@ import java.util.List; * Or predicate expression. */ public class Or extends CompoundPredicate { + /** * Desc: Constructor for CompoundPredicate. * diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java index 32285bac38..8b664ce657 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ScalarSubquery.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; import org.apache.doris.nereids.types.DataType; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java index 232164b1f9..8b54c637f4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java @@ -17,6 +17,8 @@ package org.apache.doris.nereids.trees.expressions; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; + import java.util.List; /** diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java index aa0a15631d..a7f02be03d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java @@ -138,7 +138,7 @@ public class SlotReference extends Slot { } @Override - public Expression withChildren(List children) { + public SlotReference withChildren(List children) { Preconditions.checkArgument(children.size() == 0); return this; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java index 8b036ac557..2f24f21329 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java @@ -18,17 +18,30 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.BooleanType; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.TypeCollection; +import com.google.common.collect.ImmutableList; + +import java.util.List; import java.util.Objects; /** * string regex expression. * Such as: like, regexp */ -public abstract class StringRegexPredicate extends Expression implements BinaryExpression { +public abstract class StringRegexPredicate extends Expression implements BinaryExpression, ImplicitCastInputTypes { + + // used in interface expectedInputTypes to avoid new list in each time it be called + private static final List EXPECTED_INPUT_TYPES = ImmutableList.of( + TypeCollection.CHARACTER_TYPE_COLLECTION, + TypeCollection.CHARACTER_TYPE_COLLECTION + ); /** * like or regexp @@ -52,6 +65,11 @@ public abstract class StringRegexPredicate extends Expression implements BinaryE return BooleanType.INSTANCE; } + @Override + public List expectedInputTypes() { + return EXPECTED_INPUT_TYPES; + } + @Override public String toSql() { return '(' + left().toSql() + ' ' + symbol + ' ' + right().toSql() + ')'; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java index 063c2b489f..836e13a064 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SubqueryExpr.java @@ -30,7 +30,7 @@ import java.util.Objects; /** * Subquery Expression. */ -public class SubqueryExpr extends Expression { +public abstract class SubqueryExpr extends Expression { protected LogicalPlan queryPlan; public SubqueryExpr(LogicalPlan subquery) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java index e88baed35e..0187d50210 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java @@ -17,7 +17,10 @@ package org.apache.doris.nereids.trees.expressions; +import org.apache.doris.analysis.ArithmeticExpr.Operator; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.NumericType; import com.google.common.base.Preconditions; @@ -26,15 +29,10 @@ import java.util.List; /** * Subtract Expression. BinaryExpression. */ -public class Subtract extends Arithmetic implements BinaryExpression { - public Subtract(Expression left, Expression right) { - super(ArithmeticOperator.SUBTRACT, left, right); - } +public class Subtract extends BinaryArithmetic { - @Override - public String toSql() { - return left().toSql() + ' ' + getArithmeticOperator().toString() - + ' ' + right().toSql(); + public Subtract(Expression left, Expression right) { + super(left, right, Operator.SUBTRACT); } @Override @@ -47,4 +45,9 @@ public class Subtract extends Arithmetic implements BinaryExpression { public R accept(ExpressionVisitor visitor, C context) { return visitor.visitSubtract(this, context); } + + @Override + public AbstractDataType inputType() { + return NumericType.INSTANCE; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TimestampArithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TimestampArithmetic.java index bdd4a02c0b..561d969243 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TimestampArithmetic.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TimestampArithmetic.java @@ -18,8 +18,12 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.analysis.ArithmeticExpr.Operator; -import org.apache.doris.nereids.trees.expressions.IntervalLiteral.TimeUnit; +import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.literal.IntervalLiteral.TimeUnit; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DateTimeType; import com.google.common.base.Preconditions; import org.apache.logging.log4j.LogManager; @@ -31,13 +35,14 @@ import java.util.List; * Describes the addition and subtraction of time units from timestamps. * Arithmetic expressions on timestamps are syntactic sugar. * They are executed as function call exprs in the BE. + * TODO: we need to rethink this, and maybe need to add a new type of Interval then implement IntervalLiteral as others */ public class TimestampArithmetic extends Expression implements BinaryExpression { private static final Logger LOG = LogManager.getLogger(TimestampArithmetic.class); private final String funcName; private final boolean intervalFirst; - private Operator op; - private TimeUnit timeUnit; + private final Operator op; + private final TimeUnit timeUnit; public TimestampArithmetic(String funcName, Expression e1, Expression e2, TimeUnit timeUnit) { this(funcName, null, e1, e2, timeUnit, false); @@ -45,7 +50,6 @@ public class TimestampArithmetic extends Expression implements BinaryExpression public TimestampArithmetic(Operator op, Expression e1, Expression e2, TimeUnit timeUnit, boolean intervalFirst) { this(null, op, e1, e2, timeUnit, intervalFirst); - } /** @@ -67,7 +71,7 @@ public class TimestampArithmetic extends Expression implements BinaryExpression } @Override - public Expression withChildren(List children) { + public TimestampArithmetic withChildren(List children) { Preconditions.checkArgument(children.size() == 2); return new TimestampArithmetic(this.funcName, this.op, children.get(0), children.get(1), this.timeUnit, this.intervalFirst); @@ -78,6 +82,11 @@ public class TimestampArithmetic extends Expression implements BinaryExpression this.intervalFirst); } + @Override + public DataType getDataType() throws UnboundException { + return DateTimeType.INSTANCE; + } + public String getFuncName() { return funcName; } @@ -115,16 +124,16 @@ public class TimestampArithmetic extends Expression implements BinaryExpression if (intervalFirst) { // Non-function-call like version with interval as first operand. strBuilder.append("INTERVAL "); - strBuilder.append(child(1).toSql() + " "); + strBuilder.append(child(1).toSql()).append(" "); strBuilder.append(timeUnit); strBuilder.append(" ").append(op.toString()).append(" "); strBuilder.append(child(0).toSql()); } else { // Non-function-call like version with interval as second operand. strBuilder.append(child(0).toSql()); - strBuilder.append(" " + op.toString() + " "); + strBuilder.append(" ").append(op.toString()).append(" "); strBuilder.append("INTERVAL "); - strBuilder.append(child(1).toSql() + " "); + strBuilder.append(child(1).toSql()).append(" "); strBuilder.append(timeUnit); } return strBuilder.toString(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WhenClause.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WhenClause.java index eca33acd77..be0c34c6c4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WhenClause.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/WhenClause.java @@ -18,10 +18,16 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ExpectsInputTypes; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.BooleanType; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.AnyDataType; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Objects; @@ -29,18 +35,30 @@ import java.util.Objects; /** * captures info of a single WHEN expr THEN expr clause. */ -public class WhenClause extends Expression implements BinaryExpression { +public class WhenClause extends Expression implements BinaryExpression, ExpectsInputTypes { + + public static final List EXPECTS_INPUT_TYPES + = ImmutableList.of(BooleanType.INSTANCE, AnyDataType.INSTANCE); + public WhenClause(Expression operand, Expression result) { super(operand, result); } + public Expression getOperand() { + return left(); + } + + public Expression getResult() { + return right(); + } + @Override public String toSql() { return "WHEN " + left().toSql() + " THEN " + right().toSql(); } @Override - public Expression withChildren(List children) { + public WhenClause withChildren(List children) { Preconditions.checkArgument(children.size() == 2); return new WhenClause(children.get(0), children.get(1)); } @@ -50,7 +68,6 @@ public class WhenClause extends Expression implements BinaryExpression { return visitor.visitWhenClause(this, context); } - @Override public DataType getDataType() { // when left() then right() @@ -64,6 +81,11 @@ public class WhenClause extends Expression implements BinaryExpression { return right().nullable(); } + @Override + public List expectedInputTypes() { + return EXPECTS_INPUT_TYPES; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Avg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Avg.java index 5c249706c0..c61fedf1a2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Avg.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Avg.java @@ -18,17 +18,30 @@ package org.apache.doris.nereids.trees.expressions.functions; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DateTimeType; +import org.apache.doris.nereids.types.DateType; +import org.apache.doris.nereids.types.DecimalType; import org.apache.doris.nereids.types.DoubleType; import org.apache.doris.nereids.types.VarcharType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.NumericType; +import org.apache.doris.nereids.types.coercion.TypeCollection; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import java.util.List; /** avg agg function. */ -public class Avg extends AggregateFunction implements UnaryExpression { +public class Avg extends AggregateFunction implements UnaryExpression, ImplicitCastInputTypes { + + // used in interface expectedInputTypes to avoid new list in each time it be called + private static final List EXPECTED_INPUT_TYPES = ImmutableList.of( + new TypeCollection(NumericType.INSTANCE, DateTimeType.INSTANCE, DateType.INSTANCE) + ); public Avg(Expression child) { super("avg", child); @@ -36,7 +49,15 @@ public class Avg extends AggregateFunction implements UnaryExpression { @Override public DataType getDataType() { - return DoubleType.INSTANCE; + if (child().getDataType() instanceof DecimalType) { + return child().getDataType(); + } else if (child().getDataType().isDate()) { + return DateType.INSTANCE; + } else if (child().getDataType().isDateTime()) { + return DateTimeType.INSTANCE; + } else { + return DoubleType.INSTANCE; + } } @Override @@ -54,4 +75,9 @@ public class Avg extends AggregateFunction implements UnaryExpression { public DataType getIntermediateType() { return VarcharType.createVarcharType(-1); } + + @Override + public List expectedInputTypes() { + return EXPECTED_INPUT_TYPES; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Min.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Min.java index 6f8b266b41..857ee054eb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Min.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Min.java @@ -18,7 +18,7 @@ package org.apache.doris.nereids.trees.expressions.functions; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; import org.apache.doris.nereids.types.DataType; import com.google.common.base.Preconditions; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java index 713e3d9efc..d33a66e6a9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java @@ -18,19 +18,31 @@ package org.apache.doris.nereids.trees.expressions.functions; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; -import org.apache.doris.nereids.trees.expressions.TernaryExpression; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.IntegerType; import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.TypeCollection; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import java.util.List; /** * substring function. */ -public class Substring extends BoundFunction implements TernaryExpression { +public class Substring extends BoundFunction implements TernaryExpression, ImplicitCastInputTypes { + + // used in interface expectedInputTypes to avoid new list in each time it be called + private static final List EXPECTED_INPUT_TYPES = ImmutableList.of( + TypeCollection.CHARACTER_TYPE_COLLECTION, + IntegerType.INSTANCE, + IntegerType.INSTANCE + ); public Substring(Expression str, Expression pos, Expression len) { super("substring", str, pos, len); @@ -51,11 +63,16 @@ public class Substring extends BoundFunction implements TernaryExpression { } @Override - public Expression withChildren(List children) { + public Substring withChildren(List children) { Preconditions.checkArgument(children.size() == 2 || children.size() == 3); if (children.size() == 2) { return new Substring(children.get(0), children.get(1)); } return new Substring(children.get(0), children.get(1), children.get(2)); } + + @Override + public List expectedInputTypes() { + return EXPECTED_INPUT_TYPES; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Sum.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Sum.java index c21c3cf170..9c31349d09 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Sum.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Sum.java @@ -18,19 +18,27 @@ package org.apache.doris.nereids.trees.expressions.functions; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.types.DataType; import org.apache.doris.nereids.types.DoubleType; -import org.apache.doris.nereids.types.FractionalType; -import org.apache.doris.nereids.types.IntegralType; +import org.apache.doris.nereids.types.LargeIntType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.FractionalType; +import org.apache.doris.nereids.types.coercion.IntegralType; +import org.apache.doris.nereids.types.coercion.NumericType; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import java.util.List; /** sum agg function. */ -public class Sum extends AggregateFunction implements UnaryExpression { +public class Sum extends AggregateFunction implements UnaryExpression, ImplicitCastInputTypes { + + // used in interface expectedInputTypes to avoid new list in each time it be called + private static final List EXPECTED_INPUT_TYPES = ImmutableList.of(NumericType.INSTANCE); public Sum(Expression child) { super("sum", child); @@ -39,7 +47,9 @@ public class Sum extends AggregateFunction implements UnaryExpression { @Override public DataType getDataType() { DataType dataType = child().getDataType(); - if (dataType instanceof IntegralType) { + if (dataType instanceof LargeIntType) { + return dataType; + } else if (dataType instanceof IntegralType) { return BigIntType.INSTANCE; } else if (dataType instanceof FractionalType) { // TODO: precision + 10 @@ -54,6 +64,11 @@ public class Sum extends AggregateFunction implements UnaryExpression { return child().nullable(); } + @Override + public List expectedInputTypes() { + return EXPECTED_INPUT_TYPES; + } + @Override public Expression withChildren(List children) { Preconditions.checkArgument(children.size() == 1); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Year.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Year.java index 13f52fb8af..7271696864 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Year.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Year.java @@ -18,18 +18,29 @@ package org.apache.doris.nereids.trees.expressions.functions; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DateTimeType; +import org.apache.doris.nereids.types.DateType; import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.TypeCollection; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import java.util.List; /** * year function. */ -public class Year extends BoundFunction implements UnaryExpression { +public class Year extends BoundFunction implements UnaryExpression, ImplicitCastInputTypes { + + // used in interface expectedInputTypes to avoid new list in each time it be called + private static final List EXPECTED_INPUT_TYPES = ImmutableList.of( + new TypeCollection(DateType.INSTANCE, DateTimeType.INSTANCE) + ); public Year(Expression child) { super("year", child); @@ -46,8 +57,13 @@ public class Year extends BoundFunction implements UnaryExpression { } @Override - public Expression withChildren(List children) { + public Year withChildren(List children) { Preconditions.checkArgument(children.size() == 1); return new Year(children.get(0)); } + + @Override + public List expectedInputTypes() { + return EXPECTED_INPUT_TYPES; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BigIntLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/BigIntLiteral.java similarity index 96% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BigIntLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/BigIntLiteral.java index ce3b80c974..dbe21b420b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BigIntLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/BigIntLiteral.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.IntLiteral; import org.apache.doris.analysis.LiteralExpr; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BooleanLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/BooleanLiteral.java similarity index 96% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BooleanLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/BooleanLiteral.java index 8eeacb4642..235956b766 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BooleanLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/BooleanLiteral.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.BoolLiteral; import org.apache.doris.analysis.LiteralExpr; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/CharLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/CharLiteral.java new file mode 100644 index 0000000000..dfb4236abd --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/CharLiteral.java @@ -0,0 +1,56 @@ +// 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.trees.expressions.literal; + +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.analysis.StringLiteral; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.CharType; + +import com.google.common.base.Preconditions; + +import java.util.Objects; + +/** + * char type literal + */ +public class CharLiteral extends Literal { + + private final String value; + + public CharLiteral(String value, int len) { + super(CharType.createCharType(len)); + this.value = Objects.requireNonNull(value); + Preconditions.checkArgument(value.length() <= len); + } + + @Override + public String getValue() { + return value; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitCharLiteral(this, context); + } + + @Override + public LiteralExpr toLegacyLiteral() { + return new StringLiteral(value); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DateLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java similarity index 93% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DateLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java index b3c5fddcbc..e7a4b98395 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DateLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java @@ -15,10 +15,12 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; import org.apache.doris.nereids.types.DateTimeType; import org.apache.doris.nereids.types.DateType; @@ -131,6 +133,11 @@ public class DateLiteral extends Literal { return new DateLiteral(this, type); } + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitDateLiteral(this, context); + } + @Override public Long getValue() { return (year * 10000 + month * 100 + day) * 1000000L; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DateTimeLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java similarity index 94% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DateTimeLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java index 3c9f7bcf85..60e84b66d8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DateTimeLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java @@ -15,10 +15,12 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; import org.apache.doris.nereids.types.DateTimeType; import org.apache.doris.nereids.types.DateType; @@ -123,6 +125,11 @@ public class DateTimeLiteral extends DateLiteral { return this; } + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitDateTimeLiteral(this, context); + } + @Override public Long getValue() { return (year * 10000 + month * 100 + day) * 1000000L + hour * 10000 + minute * 100 + second; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java new file mode 100644 index 0000000000..174d1fdcb8 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteral.java @@ -0,0 +1,53 @@ +// 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.trees.expressions.literal; + +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DecimalType; + +import java.math.BigDecimal; +import java.util.Objects; + +/** + * decimal type literal + */ +public class DecimalLiteral extends Literal { + + private final BigDecimal value; + + public DecimalLiteral(BigDecimal value) { + super(DecimalType.createDecimalType(value)); + this.value = Objects.requireNonNull(value); + } + + @Override + public BigDecimal getValue() { + return value; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitDecimalLiteral(this, context); + } + + @Override + public LiteralExpr toLegacyLiteral() { + return new org.apache.doris.analysis.DecimalLiteral(value); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DoubleLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java similarity index 96% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DoubleLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java index 4a5b9c298e..34b8ca36fb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DoubleLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.FloatLiteral; import org.apache.doris.analysis.LiteralExpr; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteral.java new file mode 100644 index 0000000000..4fff7445ef --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteral.java @@ -0,0 +1,51 @@ +// 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.trees.expressions.literal; + +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.FloatType; + +/** + * float type literal + */ +public class FloatLiteral extends Literal { + + private final float value; + + public FloatLiteral(float value) { + super(FloatType.INSTANCE); + this.value = value; + } + + @Override + public Float getValue() { + return value; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitFloatLiteral(this, context); + } + + @Override + public LiteralExpr toLegacyLiteral() { + return new org.apache.doris.analysis.FloatLiteral((double) value, Type.FLOAT); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IntegerLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IntegerLiteral.java similarity index 96% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IntegerLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IntegerLiteral.java index 8dbbcfb533..6af432d832 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IntegerLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IntegerLiteral.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.IntLiteral; import org.apache.doris.analysis.LiteralExpr; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IntervalLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IntervalLiteral.java similarity index 83% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IntervalLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IntervalLiteral.java index a11d408bcc..8c70ca162c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/IntervalLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IntervalLiteral.java @@ -15,7 +15,12 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; + +import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DateType; /** * Interval for timestamp calculation. @@ -29,6 +34,11 @@ public class IntervalLiteral extends Expression { this.timeUnit = TimeUnit.valueOf(desc.toUpperCase()); } + @Override + public DataType getDataType() throws UnboundException { + return DateType.INSTANCE; + } + public Expression value() { return value; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/LargeIntLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/LargeIntLiteral.java new file mode 100644 index 0000000000..c839962040 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/LargeIntLiteral.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.doris.nereids.trees.expressions.literal; + +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.LargeIntType; + +import java.math.BigInteger; +import java.util.Objects; + +/** + * large int type literal + */ +public class LargeIntLiteral extends Literal { + + private final BigInteger value; + + public LargeIntLiteral(BigInteger value) { + super(LargeIntType.INSTANCE); + this.value = Objects.requireNonNull(value); + } + + @Override + public BigInteger getValue() { + return value; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitLargeIntLiteral(this, context); + } + + @Override + public LiteralExpr toLegacyLiteral() { + try { + return new org.apache.doris.analysis.LargeIntLiteral(value.toString()); + } catch (AnalysisException e) { + throw new org.apache.doris.nereids.exceptions.AnalysisException( + "Can not convert to legacy literal: " + value, e); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java similarity index 94% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java index e7c62720f2..c80d2a30db 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java @@ -15,10 +15,12 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/NullLiteral.java similarity index 92% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/NullLiteral.java index 68c5fdbf37..8d980cb779 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/NullLiteral.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; @@ -26,6 +26,8 @@ import org.apache.doris.nereids.types.NullType; */ public class NullLiteral extends Literal { + public static final NullLiteral INSTANCE = new NullLiteral(); + public NullLiteral() { super(NullType.INSTANCE); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/SmallIntLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/SmallIntLiteral.java new file mode 100644 index 0000000000..15a36971f1 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/SmallIntLiteral.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.doris.nereids.trees.expressions.literal; + +import org.apache.doris.analysis.IntLiteral; +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.catalog.Type; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.SmallIntType; + +/** + * small int type literal + */ +public class SmallIntLiteral extends Literal { + + private final short value; + + public SmallIntLiteral(short value) { + super(SmallIntType.INSTANCE); + this.value = value; + } + + @Override + public Short getValue() { + return value; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitSmallIntLiteral(this, context); + } + + @Override + public LiteralExpr toLegacyLiteral() { + try { + return new IntLiteral(value, Type.SMALLINT); + } catch (AnalysisException e) { + throw new org.apache.doris.nereids.exceptions.AnalysisException( + "Can not convert to legacy literal: " + value, e); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLiteral.java similarity index 94% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringLiteral.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLiteral.java index 1e0b5cfea0..bc19a7f506 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLiteral.java @@ -15,10 +15,11 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; import org.apache.doris.nereids.types.StringType; @@ -80,7 +81,7 @@ public class StringLiteral extends Literal { } @Override - public String toSql() { + public String toString() { return "'" + value + "'"; } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TinyIntLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TinyIntLiteral.java new file mode 100644 index 0000000000..414255f7a6 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TinyIntLiteral.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.doris.nereids.trees.expressions.literal; + +import org.apache.doris.analysis.IntLiteral; +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.catalog.Type; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.TinyIntType; + +/** + * tiny int type literal + */ +public class TinyIntLiteral extends Literal { + + private final byte value; + + public TinyIntLiteral(byte value) { + super(TinyIntType.INSTANCE); + this.value = value; + } + + @Override + public Byte getValue() { + return value; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitTinyIntLiteral(this, context); + } + + @Override + public LiteralExpr toLegacyLiteral() { + try { + return new IntLiteral(value, Type.TINYINT); + } catch (AnalysisException e) { + throw new org.apache.doris.nereids.exceptions.AnalysisException( + "Can not convert to legacy literal: " + value, e); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java new file mode 100644 index 0000000000..7d43a3cd4d --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java @@ -0,0 +1,61 @@ +// 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.trees.expressions.literal; + +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.analysis.StringLiteral; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.VarcharType; + +import com.google.common.base.Preconditions; + +import java.util.Objects; + +/** + * varchar type literal + */ +public class VarcharLiteral extends Literal { + + private final String value; + + public VarcharLiteral(String value) { + super(VarcharType.SYSTEM_DEFAULT); + this.value = Objects.requireNonNull(value); + } + + public VarcharLiteral(String value, int len) { + super(VarcharType.createVarcharType(len)); + this.value = Objects.requireNonNull(value); + Preconditions.checkArgument(value.length() <= len); + } + + @Override + public String getValue() { + return value; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitVarcharLiteral(this, context); + } + + @Override + public LiteralExpr toLegacyLiteral() { + return new StringLiteral(value); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/BinaryExpression.java similarity index 89% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryExpression.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/BinaryExpression.java index 8965e36b6f..06772e3791 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryExpression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/BinaryExpression.java @@ -15,9 +15,10 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.shape; import org.apache.doris.nereids.trees.BinaryNode; +import org.apache.doris.nereids.trees.expressions.Expression; /** * Interface for all expression that have two children. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LeafExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/LeafExpression.java similarity index 89% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LeafExpression.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/LeafExpression.java index 50fd0373f3..2914a9a814 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LeafExpression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/LeafExpression.java @@ -15,9 +15,10 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.shape; import org.apache.doris.nereids.trees.LeafNode; +import org.apache.doris.nereids.trees.expressions.Expression; /** * Interface for all expression that have no child. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TernaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/TernaryExpression.java similarity index 89% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TernaryExpression.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/TernaryExpression.java index bea2644e54..38b0b2ec9b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TernaryExpression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/TernaryExpression.java @@ -15,9 +15,10 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.shape; import org.apache.doris.nereids.trees.TernaryNode; +import org.apache.doris.nereids.trees.expressions.Expression; /** * Interface for all expression that have three children. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/UnaryExpression.java similarity index 89% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/UnaryExpression.java index ba37beee60..b116288b25 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/shape/UnaryExpression.java @@ -15,9 +15,10 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.expressions; +package org.apache.doris.nereids.trees.expressions.shape; import org.apache.doris.nereids.trees.UnaryNode; +import org.apache.doris.nereids.trees.expressions.Expression; /** * Abstract class for all expression that have one child. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/ExpectsInputTypes.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/ExpectsInputTypes.java new file mode 100644 index 0000000000..ad8abbff24 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/ExpectsInputTypes.java @@ -0,0 +1,37 @@ +// 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.trees.expressions.typecoercion; + +import org.apache.doris.nereids.types.coercion.AbstractDataType; + +import java.util.List; + +/** + * An interface to define the expected input types of an expression. + * + * This interface is typically used by operator expressions + * (e.g. {@link org.apache.doris.nereids.trees.expressions.Add}) + * to define expected input types without any implicit casting. + * + * Most function expressions (e.g. {@link org.apache.doris.nereids.trees.expressions.functions.Substring} + * should extend {@link ImplicitCastInputTypes}) instead. + */ +public interface ExpectsInputTypes { + + List expectedInputTypes(); +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/FractionalType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/ImplicitCastInputTypes.java similarity index 77% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/types/FractionalType.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/ImplicitCastInputTypes.java index bc6e95b59c..a13a35a79b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/FractionalType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/ImplicitCastInputTypes.java @@ -15,10 +15,10 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.types; +package org.apache.doris.nereids.trees.expressions.typecoercion; /** - * Abstract for all fractional type in Nereids. + * an interface used to identify whether the current expression can be implicitly cast input types. */ -public abstract class FractionalType extends NumericType { +public interface ImplicitCastInputTypes extends ExpectsInputTypes { } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/TypeCheckResult.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/TypeCheckResult.java new file mode 100644 index 0000000000..e7e5e9bfc0 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/typecoercion/TypeCheckResult.java @@ -0,0 +1,49 @@ +// 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.trees.expressions.typecoercion; + +import java.util.Optional; + +/** + * Save type check result. + */ +public class TypeCheckResult { + + public static TypeCheckResult SUCCESS = new TypeCheckResult(true, null); + + final boolean success; + + final Optional message; + + public TypeCheckResult(boolean success, String message) { + this.success = success; + this.message = Optional.ofNullable(message); + } + + public boolean success() { + return success; + } + + public boolean failed() { + return !success; + } + + public String getMessage() { + return message.orElseGet(String::new); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/DefaultSubExprRewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/DefaultSubExprRewriter.java index b21b1df9a3..919e9095db 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/DefaultSubExprRewriter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/DefaultSubExprRewriter.java @@ -21,6 +21,7 @@ import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.memo.Memo; import org.apache.doris.nereids.rules.analysis.Scope; +import org.apache.doris.nereids.trees.expressions.Exists; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.InSubquery; import org.apache.doris.nereids.trees.expressions.ListQuery; @@ -43,8 +44,8 @@ public class DefaultSubExprRewriter extends DefaultExpressionRewriter { } @Override - public Expression visitSubqueryExpr(SubqueryExpr expr, C context) { - return new SubqueryExpr(analyzeSubquery(expr)); + public Expression visitExistsSubquery(Exists exists, C context) { + return new Exists(analyzeSubquery(exists)); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java index aa6dd439b0..d3f24956fb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java @@ -24,18 +24,14 @@ import org.apache.doris.nereids.analyzer.UnboundStar; import org.apache.doris.nereids.trees.expressions.Add; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.And; -import org.apache.doris.nereids.trees.expressions.Arithmetic; import org.apache.doris.nereids.trees.expressions.Between; -import org.apache.doris.nereids.trees.expressions.BigIntLiteral; -import org.apache.doris.nereids.trees.expressions.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.BinaryArithmetic; +import org.apache.doris.nereids.trees.expressions.BinaryOperator; import org.apache.doris.nereids.trees.expressions.CaseWhen; import org.apache.doris.nereids.trees.expressions.Cast; import org.apache.doris.nereids.trees.expressions.ComparisonPredicate; import org.apache.doris.nereids.trees.expressions.CompoundPredicate; -import org.apache.doris.nereids.trees.expressions.DateLiteral; -import org.apache.doris.nereids.trees.expressions.DateTimeLiteral; import org.apache.doris.nereids.trees.expressions.Divide; -import org.apache.doris.nereids.trees.expressions.DoubleLiteral; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Exists; import org.apache.doris.nereids.trees.expressions.Expression; @@ -43,24 +39,20 @@ import org.apache.doris.nereids.trees.expressions.GreaterThan; import org.apache.doris.nereids.trees.expressions.GreaterThanEqual; import org.apache.doris.nereids.trees.expressions.InPredicate; import org.apache.doris.nereids.trees.expressions.InSubquery; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.LessThan; import org.apache.doris.nereids.trees.expressions.LessThanEqual; import org.apache.doris.nereids.trees.expressions.Like; import org.apache.doris.nereids.trees.expressions.ListQuery; -import org.apache.doris.nereids.trees.expressions.Literal; import org.apache.doris.nereids.trees.expressions.Mod; import org.apache.doris.nereids.trees.expressions.Multiply; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Not; -import org.apache.doris.nereids.trees.expressions.NullLiteral; import org.apache.doris.nereids.trees.expressions.NullSafeEqual; import org.apache.doris.nereids.trees.expressions.Or; import org.apache.doris.nereids.trees.expressions.Regexp; import org.apache.doris.nereids.trees.expressions.ScalarSubquery; import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotReference; -import org.apache.doris.nereids.trees.expressions.StringLiteral; import org.apache.doris.nereids.trees.expressions.StringRegexPredicate; import org.apache.doris.nereids.trees.expressions.SubqueryExpr; import org.apache.doris.nereids.trees.expressions.Subtract; @@ -68,6 +60,22 @@ import org.apache.doris.nereids.trees.expressions.TimestampArithmetic; import org.apache.doris.nereids.trees.expressions.WhenClause; import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction; import org.apache.doris.nereids.trees.expressions.functions.BoundFunction; +import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.CharLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DateLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral; +import org.apache.doris.nereids.trees.expressions.literal.FloatLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; +import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; /** * Use the visitor to visit expression and forward to unified method(visitExpression). @@ -80,8 +88,12 @@ public abstract class ExpressionVisitor { return visitNamedExpression(alias, context); } + public R visitBinaryOperator(BinaryOperator binaryOperator, C context) { + return visit(binaryOperator, context); + } + public R visitComparisonPredicate(ComparisonPredicate cp, C context) { - return visit(cp, context); + return visitBinaryOperator(cp, context); } public R visitEqualTo(EqualTo equalTo, C context) { @@ -128,14 +140,34 @@ public abstract class ExpressionVisitor { return visit(literal, context); } + public R visitNullLiteral(NullLiteral nullLiteral, C context) { + return visitLiteral(nullLiteral, context); + } + public R visitBooleanLiteral(BooleanLiteral booleanLiteral, C context) { return visitLiteral(booleanLiteral, context); } + public R visitCharLiteral(CharLiteral charLiteral, C context) { + return visitLiteral(charLiteral, context); + } + + public R visitVarcharLiteral(VarcharLiteral varcharLiteral, C context) { + return visitLiteral(varcharLiteral, context); + } + public R visitStringLiteral(StringLiteral stringLiteral, C context) { return visitLiteral(stringLiteral, context); } + public R visitTinyIntLiteral(TinyIntLiteral tinyIntLiteral, C context) { + return visitLiteral(tinyIntLiteral, context); + } + + public R visitSmallIntLiteral(SmallIntLiteral smallIntLiteral, C context) { + return visitLiteral(smallIntLiteral, context); + } + public R visitIntegerLiteral(IntegerLiteral integerLiteral, C context) { return visitLiteral(integerLiteral, context); } @@ -144,8 +176,16 @@ public abstract class ExpressionVisitor { return visitLiteral(bigIntLiteral, context); } - public R visitNullLiteral(NullLiteral nullLiteral, C context) { - return visitLiteral(nullLiteral, context); + public R visitLargeIntLiteral(LargeIntLiteral largeIntLiteral, C context) { + return visitLiteral(largeIntLiteral, context); + } + + public R visitDecimalLiteral(DecimalLiteral decimalLiteral, C context) { + return visitLiteral(decimalLiteral, context); + } + + public R visitFloatLiteral(FloatLiteral floatLiteral, C context) { + return visitLiteral(floatLiteral, context); } public R visitDoubleLiteral(DoubleLiteral doubleLiteral, C context) { @@ -165,7 +205,7 @@ public abstract class ExpressionVisitor { } public R visitCompoundPredicate(CompoundPredicate compoundPredicate, C context) { - return visit(compoundPredicate, context); + return visitBinaryOperator(compoundPredicate, context); } public R visitAnd(And and, C context) { @@ -200,28 +240,28 @@ public abstract class ExpressionVisitor { return visitBoundFunction(aggregateFunction, context); } - public R visitArithmetic(Arithmetic arithmetic, C context) { - return visit(arithmetic, context); + public R visitBinaryArithmetic(BinaryArithmetic binaryArithmetic, C context) { + return visitBinaryOperator(binaryArithmetic, context); } public R visitAdd(Add add, C context) { - return visitArithmetic(add, context); + return visitBinaryArithmetic(add, context); } public R visitSubtract(Subtract subtract, C context) { - return visitArithmetic(subtract, context); + return visitBinaryArithmetic(subtract, context); } public R visitMultiply(Multiply multiply, C context) { - return visitArithmetic(multiply, context); + return visitBinaryArithmetic(multiply, context); } public R visitDivide(Divide divide, C context) { - return visitArithmetic(divide, context); + return visitBinaryArithmetic(divide, context); } public R visitMod(Mod mod, C context) { - return visitArithmetic(mod, context); + return visitBinaryArithmetic(mod, context); } public R visitWhenClause(WhenClause whenClause, C context) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java index 6b78ff2ab5..e65eddf73e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java @@ -20,8 +20,8 @@ package org.apache.doris.nereids.trees.plans.logical; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.algebra.Limit; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java index 6124bd7da9..727dc18054 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLimit.java @@ -20,7 +20,7 @@ package org.apache.doris.nereids.trees.plans.physical; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.PlanType; import org.apache.doris.nereids.trees.plans.algebra.Limit; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BigIntType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BigIntType.java index 4c4a79efb9..4dae2b79b8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BigIntType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BigIntType.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.IntegralType; /** * BigInt data type in Nereids. @@ -29,5 +30,19 @@ public class BigIntType extends IntegralType { public Type toCatalogDataType() { return Type.BIGINT; } -} + @Override + public boolean acceptsType(DataType other) { + return other instanceof BigIntType; + } + + @Override + public String simpleString() { + return "bigint"; + } + + @Override + public DataType defaultConcreteType() { + return this; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BooleanType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BooleanType.java index b236ba12a4..2e381cc117 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BooleanType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BooleanType.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.PrimitiveType; /** * Boolean type in Nereids. @@ -29,4 +30,9 @@ public class BooleanType extends PrimitiveType { public Type toCatalogDataType() { return Type.BOOLEAN; } + + @Override + public String simpleString() { + return "boolean"; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/CharType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/CharType.java new file mode 100644 index 0000000000..fc25ddedfa --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/CharType.java @@ -0,0 +1,79 @@ +// 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.types; + +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.CharacterType; + +import java.util.Objects; + +/** + * Char type in Nereids. + */ +public class CharType extends CharacterType { + + public static final CharType SYSTEM_DEFAULT = new CharType(-1); + + public CharType(int len) { + super(len); + } + + public static CharType createCharType(int len) { + return new CharType(len); + } + + @Override + public Type toCatalogDataType() { + return ScalarType.createChar(len); + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof CharType; + } + + @Override + public String simpleString() { + return "char"; + } + + @Override + public DataType defaultConcreteType() { + return this; + } + + @Override + public boolean equals(Object o) { + if (!super.equals(o)) { + return false; + } + CharType charType = (CharType) o; + return len == charType.len; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), len); + } + + @Override + public String toSql() { + return "CHAR(" + len + ")"; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java index a08fa9759c..74f26bdd70 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java @@ -24,11 +24,31 @@ import org.apache.doris.catalog.ScalarType; import org.apache.doris.catalog.StructType; import org.apache.doris.catalog.Type; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.CharacterType; +import org.apache.doris.nereids.types.coercion.NumericType; +import org.apache.doris.nereids.types.coercion.PrimitiveType; + +import com.google.common.collect.ImmutableMap; + +import java.util.Locale; +import java.util.Map; +import java.util.function.Supplier; /** * Abstract class for all data type in Nereids. */ -public abstract class DataType { +public abstract class DataType implements AbstractDataType { + + // use class and supplier here to avoid class load deadlock. + private static final Map, Supplier> PROMOTION_MAP + = ImmutableMap., Supplier>builder() + .put(TinyIntType.class, () -> SmallIntType.INSTANCE) + .put(SmallIntType.class, () -> IntegerType.INSTANCE) + .put(IntegerType.class, () -> BigIntType.INSTANCE) + .put(FloatType.class, () -> DoubleType.INSTANCE) + .build(); + /** * Convert data type in Doris catalog to data type in Nereids. * TODO: throw exception when cannot convert catalog type to Nereids type @@ -42,12 +62,22 @@ public abstract class DataType { switch (scalarType.getPrimitiveType()) { case BOOLEAN: return BooleanType.INSTANCE; + case TINYINT: + return TinyIntType.INSTANCE; + case SMALLINT: + return SmallIntType.INSTANCE; case INT: return IntegerType.INSTANCE; case BIGINT: return BigIntType.INSTANCE; + case LARGEINT: + return LargeIntType.INSTANCE; + case FLOAT: + return FloatType.INSTANCE; case DOUBLE: return DoubleType.INSTANCE; + case CHAR: + return CharType.createCharType(scalarType.getLength()); case VARCHAR: return VarcharType.createVarcharType(scalarType.getLength()); case STRING: @@ -85,6 +115,7 @@ public abstract class DataType { */ public static DataType convertFromString(String type) { // TODO: use a better way to resolve types + // TODO: support varchar, char, decimal switch (type.toLowerCase()) { case "bool": case "boolean": @@ -108,6 +139,39 @@ public abstract class DataType { public abstract Type toCatalogDataType(); + public abstract String toSql(); + + @Override + public String toString() { + return toSql(); + } + + public String typeName() { + return this.getClass().getSimpleName().replace("Type", "").toLowerCase(Locale.ROOT); + } + + @Override + public DataType defaultConcreteType() { + return this; + } + + @Override + public boolean acceptsType(DataType other) { + return sameType(other); + } + + /** + * this and other is same type. + */ + private boolean sameType(DataType other) { + return this.equals(other); + } + + @Override + public String simpleString() { + return typeName(); + } + @Override public boolean equals(Object o) { if (this == o) { @@ -139,4 +203,28 @@ public abstract class DataType { public boolean isDateType() { return isDate() || isDateTime(); } + + public boolean isNullType() { + return this instanceof NullType; + } + + public boolean isNumericType() { + return this instanceof NumericType; + } + + public boolean isStringType() { + return this instanceof CharacterType; + } + + public boolean isPrimitive() { + return this instanceof PrimitiveType; + } + + public DataType promotion() { + if (PROMOTION_MAP.containsKey(this.getClass())) { + return PROMOTION_MAP.get(this.getClass()).get(); + } else { + return this; + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeType.java index 0d05006e50..90d77ae4b4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeType.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.PrimitiveType; /** * Datetime type in Nereids. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateType.java index 75187b4bab..4eddbc4520 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateType.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.PrimitiveType; /** * Date type in Nereids. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalType.java index f12d550698..29c93083de 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalType.java @@ -18,22 +18,80 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.FractionalType; +import org.apache.doris.nereids.types.coercion.IntegralType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; + +import java.math.BigDecimal; +import java.util.Map; +import java.util.Objects; /** * Decimal type in Nereids. */ public class DecimalType extends FractionalType { - private int precision; - private int scale; + public static int MAX_PRECISION = 38; + public static int MAX_SCALE = 38; + public static DecimalType SYSTEM_DEFAULT = new DecimalType(MAX_PRECISION, 18); + + private static final DecimalType BOOLEAN_DECIMAL = new DecimalType(1, 0); + private static final DecimalType TINYINT_DECIMAL = new DecimalType(3, 0); + private static final DecimalType SMALLINT_DECIMAL = new DecimalType(5, 0); + private static final DecimalType INTEGER_DECIMAL = new DecimalType(10, 0); + private static final DecimalType BIGINT_DECIMAL = new DecimalType(20, 0); + private static final DecimalType LARGEINT_DECIMAL = new DecimalType(38, 0); + private static final DecimalType FLOAT_DECIMAL = new DecimalType(14, 7); + private static final DecimalType DOUBLE_DECIMAL = new DecimalType(30, 15); + + private static final Map FOR_TYPE_MAP = ImmutableMap.builder() + .put(TinyIntType.INSTANCE, TINYINT_DECIMAL) + .put(SmallIntType.INSTANCE, SMALLINT_DECIMAL) + .put(IntegerType.INSTANCE, INTEGER_DECIMAL) + .put(BigIntType.INSTANCE, BIGINT_DECIMAL) + .put(LargeIntType.INSTANCE, LARGEINT_DECIMAL) + .put(FloatType.INSTANCE, FLOAT_DECIMAL) + .put(DoubleType.INSTANCE, DOUBLE_DECIMAL) + .build(); + + private final int precision; + private final int scale; public DecimalType(int precision, int scale) { + Preconditions.checkArgument(precision >= scale); + Preconditions.checkArgument(precision > 0 && precision <= MAX_PRECISION); + Preconditions.checkArgument(scale >= 0 && scale <= MAX_SCALE); this.precision = precision; this.scale = scale; } public static DecimalType createDecimalType(int precision, int scale) { - return new DecimalType(precision, scale); + return new DecimalType(Math.min(precision, MAX_PRECISION), Math.min(scale, MAX_SCALE)); + } + + public static DecimalType createDecimalType(BigDecimal bigDecimal) { + int precision = org.apache.doris.analysis.DecimalLiteral.getBigDecimalPrecision(bigDecimal); + int scale = org.apache.doris.analysis.DecimalLiteral.getBigDecimalScale(bigDecimal); + return createDecimalType(precision, scale); + } + + public static DecimalType forType(DataType dataType) { + if (FOR_TYPE_MAP.containsKey(dataType)) { + return FOR_TYPE_MAP.get(dataType); + } + throw new RuntimeException("Could not create decimal for type " + dataType); + } + + public static DecimalType widerDecimalType(DecimalType left, DecimalType right) { + return widerDecimalType(left.getPrecision(), right.getPrecision(), left.getScale(), right.getScale()); + } + + private static DecimalType widerDecimalType(int leftPrecision, int rightPrecision, int leftScale, int rightScale) { + int scale = Math.max(leftScale, rightScale); + int range = Math.max(leftPrecision - leftScale, rightPrecision - rightScale); + return DecimalType.createDecimalType(range + scale, scale); } @Override @@ -48,5 +106,59 @@ public class DecimalType extends FractionalType { public int getScale() { return scale; } + + public boolean isWiderThan(DataType other) { + return isWiderThanInternal(other); + } + + private boolean isWiderThanInternal(DataType other) { + if (other instanceof DecimalType) { + DecimalType dt = (DecimalType) other; + return this.precision - this.scale >= dt.precision - dt.scale && this.scale >= dt.scale; + } else if (other instanceof IntegralType) { + return isWiderThanInternal(forType(other)); + } + return false; + } + + @Override + public DataType defaultConcreteType() { + return SYSTEM_DEFAULT; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof DecimalType; + } + + @Override + public String simpleString() { + return "decimal"; + } + + @Override + public String toSql() { + return "DECIMAL(" + precision + ", " + scale + ")"; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + DecimalType that = (DecimalType) o; + return precision == that.precision && scale == that.scale; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), precision, scale); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java index b953e8dd58..3017c1792c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.FractionalType; /** * Double data type in Nereids. @@ -29,4 +30,24 @@ public class DoubleType extends FractionalType { public Type toCatalogDataType() { return Type.DOUBLE; } + + @Override + public boolean equals(Object o) { + return o instanceof DoubleType; + } + + @Override + public String simpleString() { + return "double"; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof DoubleType; + } + + @Override + public DataType defaultConcreteType() { + return this; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/FloatType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/FloatType.java new file mode 100644 index 0000000000..de2e54166f --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/FloatType.java @@ -0,0 +1,53 @@ +// 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.types; + +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.FractionalType; + +/** + * Float type in Nereids. + */ +public class FloatType extends FractionalType { + public static FloatType INSTANCE = new FloatType(); + + @Override + public Type toCatalogDataType() { + return Type.FLOAT; + } + + @Override + public boolean equals(Object o) { + return o instanceof FloatType; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof FloatType; + } + + @Override + public DataType defaultConcreteType() { + return this; + } + + @Override + public String simpleString() { + return "float"; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/IntegerType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/IntegerType.java index aff70cdd0b..e9a6385346 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/IntegerType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/IntegerType.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.IntegralType; /** * Integer data type in Nereids. @@ -34,4 +35,19 @@ public class IntegerType extends IntegralType { public boolean equals(Object o) { return o instanceof IntegerType; } + + @Override + public String simpleString() { + return "int"; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof IntegerType; + } + + @Override + public DataType defaultConcreteType() { + return this; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/LargeIntType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/LargeIntType.java new file mode 100644 index 0000000000..238c6d287a --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/LargeIntType.java @@ -0,0 +1,53 @@ +// 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.types; + +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.IntegralType; + +/** + * LargeInt type in Nereids. + */ +public class LargeIntType extends IntegralType { + public static LargeIntType INSTANCE = new LargeIntType(); + + @Override + public Type toCatalogDataType() { + return Type.LARGEINT; + } + + @Override + public boolean equals(Object o) { + return o instanceof LargeIntType; + } + + @Override + public String simpleString() { + return "largeint"; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof LargeIntType; + } + + @Override + public DataType defaultConcreteType() { + return this; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/NullType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/NullType.java index cf1cb952bf..52e4d0b4ed 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/NullType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/NullType.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.PrimitiveType; /** * Null data type in Nereids. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/SmallIntType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/SmallIntType.java new file mode 100644 index 0000000000..c0cc52d6a6 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/SmallIntType.java @@ -0,0 +1,53 @@ +// 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.types; + +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.IntegralType; + +/** + * SmallInt type in Nereids. + */ +public class SmallIntType extends IntegralType { + public static SmallIntType INSTANCE = new SmallIntType(); + + @Override + public Type toCatalogDataType() { + return Type.SMALLINT; + } + + @Override + public boolean equals(Object o) { + return o instanceof SmallIntType; + } + + @Override + public String simpleString() { + return "smallint"; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof SmallIntType; + } + + @Override + public DataType defaultConcreteType() { + return this; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StringType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StringType.java index 744053acd6..62e13a952a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StringType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StringType.java @@ -18,15 +18,36 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.CharacterType; /** * String data type in Nereids. */ -public class StringType extends DataType { +public class StringType extends CharacterType { + public static StringType INSTANCE = new StringType(); + public StringType() { + super(-1); + } + @Override public Type toCatalogDataType() { return Type.STRING; } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof StringType; + } + + @Override + public String simpleString() { + return "string"; + } + + @Override + public DataType defaultConcreteType() { + return this; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TinyIntType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TinyIntType.java new file mode 100644 index 0000000000..62ec486fe7 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TinyIntType.java @@ -0,0 +1,53 @@ +// 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.types; + +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.IntegralType; + +/** + * TinyInt type in Nereids. + */ +public class TinyIntType extends IntegralType { + public static TinyIntType INSTANCE = new TinyIntType(); + + @Override + public Type toCatalogDataType() { + return Type.TINYINT; + } + + @Override + public boolean equals(Object o) { + return o instanceof TinyIntType; + } + + @Override + public String simpleString() { + return "tinyint"; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof TinyIntType; + } + + @Override + public DataType defaultConcreteType() { + return this; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/VarcharType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/VarcharType.java index 09f72ceef2..47d4588cba 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/VarcharType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/VarcharType.java @@ -19,17 +19,19 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.ScalarType; import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.coercion.CharacterType; import java.util.Objects; /** * Varchar type in Nereids. */ -public class VarcharType extends PrimitiveType { - private final int len; +public class VarcharType extends CharacterType { + + public static VarcharType SYSTEM_DEFAULT = new VarcharType(-1); public VarcharType(int len) { - this.len = len; + super(len); } public static VarcharType createVarcharType(int len) { @@ -41,6 +43,26 @@ public class VarcharType extends PrimitiveType { return ScalarType.createVarcharType(len); } + @Override + public boolean acceptsType(DataType other) { + return other instanceof VarcharType; + } + + @Override + public String simpleString() { + return "varchar"; + } + + @Override + public DataType defaultConcreteType() { + return this; + } + + @Override + public String toSql() { + return "VARCHAR(" + len + ")"; + } + @Override public boolean equals(Object o) { if (!super.equals(o)) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/NumericType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AbstractDataType.java similarity index 60% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/types/NumericType.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AbstractDataType.java index 8f641e2830..28b30a6131 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/NumericType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AbstractDataType.java @@ -15,10 +15,27 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.types; +package org.apache.doris.nereids.types.coercion; + +import org.apache.doris.nereids.types.DataType; /** - * Abstract class for all numeric type in Nereids. + * Represent a set of equality concrete data type. */ -public abstract class NumericType extends PrimitiveType { +public interface AbstractDataType { + + /** + * the default concrete type cast to when do implicit cast + */ + DataType defaultConcreteType(); + + /** + * This AbstractDataType could accept other without implicit cast + */ + boolean acceptsType(DataType other); + + /** + * simple string used to print error message + */ + String simpleString(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java new file mode 100644 index 0000000000..3c8bafbaee --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/AnyDataType.java @@ -0,0 +1,43 @@ +// 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.types.coercion; + +import org.apache.doris.nereids.types.DataType; + +/** + * Represent any datatype in type coercion. + */ +public class AnyDataType implements AbstractDataType { + + public static final AnyDataType INSTANCE = new AnyDataType(); + + @Override + public DataType defaultConcreteType() { + throw new RuntimeException("Unsupported operation."); + } + + @Override + public boolean acceptsType(DataType other) { + return true; + } + + @Override + public String simpleString() { + return "any"; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/CharacterType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/CharacterType.java new file mode 100644 index 0000000000..361c30c158 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/CharacterType.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.types.coercion; + +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.StringType; + +/** + * Abstract type for all characters type in Nereids. + */ +public class CharacterType extends PrimitiveType { + + public static final CharacterType INSTANCE = new CharacterType(-1); + + protected final int len; + + public CharacterType(int len) { + this.len = len; + } + + public int getLen() { + return len; + } + + @Override + public Type toCatalogDataType() { + throw new RuntimeException("CharacterType is only used for implicit cast."); + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof CharacterType; + } + + @Override + public DataType defaultConcreteType() { + return StringType.INSTANCE; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/FractionalType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/FractionalType.java new file mode 100644 index 0000000000..6ba9e89d4b --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/FractionalType.java @@ -0,0 +1,43 @@ +// 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.types.coercion; + +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DoubleType; + +/** + * Abstract for all fractional type in Nereids. + */ +public class FractionalType extends NumericType { + public static final FractionalType INSTANCE = new FractionalType(); + + @Override + public DataType defaultConcreteType() { + return DoubleType.INSTANCE; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof FractionalType; + } + + @Override + public String simpleString() { + return "fractional"; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/IntegralType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/IntegralType.java new file mode 100644 index 0000000000..628e881198 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/IntegralType.java @@ -0,0 +1,44 @@ +// 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.types.coercion; + +import org.apache.doris.nereids.types.BigIntType; +import org.apache.doris.nereids.types.DataType; + +/** + * Abstract class for all integral data type in Nereids. + */ +public class IntegralType extends NumericType { + + public static final IntegralType INSTANCE = new IntegralType(); + + @Override + public DataType defaultConcreteType() { + return BigIntType.INSTANCE; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof IntegralType; + } + + @Override + public String simpleString() { + return "integral"; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/NumericType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/NumericType.java new file mode 100644 index 0000000000..74733a1f1d --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/NumericType.java @@ -0,0 +1,50 @@ +// 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.types.coercion; + +import org.apache.doris.catalog.Type; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DoubleType; + +/** + * Abstract class for all numeric type in Nereids. + */ +public class NumericType extends PrimitiveType { + + public static final NumericType INSTANCE = new NumericType(); + + @Override + public Type toCatalogDataType() { + throw new RuntimeException("NumericType is only used for implicit cast."); + } + + @Override + public DataType defaultConcreteType() { + return DoubleType.INSTANCE; + } + + @Override + public boolean acceptsType(DataType other) { + return other instanceof NumericType; + } + + @Override + public String simpleString() { + return "numeric"; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/PrimitiveType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/PrimitiveType.java similarity index 79% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/types/PrimitiveType.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/PrimitiveType.java index 9551053d27..6ced44bf6b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/PrimitiveType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/PrimitiveType.java @@ -15,10 +15,18 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.types; +package org.apache.doris.nereids.types.coercion; + +import org.apache.doris.nereids.types.DataType; + +import java.util.Locale; /** * Primitive data type in Nereids. */ public abstract class PrimitiveType extends DataType { + @Override + public String toSql() { + return simpleString().toUpperCase(Locale.ROOT); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/TypeCollection.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/TypeCollection.java new file mode 100644 index 0000000000..78753d8bf6 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/TypeCollection.java @@ -0,0 +1,74 @@ +// 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.types.coercion; + +import org.apache.doris.nereids.types.CharType; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.VarcharType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * A collection of types that can be used to specify type constraints. The sequence also specifies + * precedence: an earlier type takes precedence over a latter type. + *

TypeCollection(StringType, NumericType) + *

This means that we prefer StringType over NumericType if it is possible to cast to StringType. + */ +public class TypeCollection implements AbstractDataType { + + public static final TypeCollection CHARACTER_TYPE_COLLECTION + = new TypeCollection(StringType.INSTANCE, VarcharType.SYSTEM_DEFAULT, CharType.SYSTEM_DEFAULT); + + private final List types; + + public TypeCollection(List types) { + Objects.requireNonNull(types); + Preconditions.checkArgument(!types.isEmpty()); + this.types = ImmutableList.copyOf(types); + } + + public TypeCollection(AbstractDataType... types) { + Preconditions.checkArgument(types.length > 0); + this.types = ImmutableList.copyOf(types); + } + + public List getTypes() { + return types; + } + + @Override + public DataType defaultConcreteType() { + return types.get(0).defaultConcreteType(); + } + + @Override + public boolean acceptsType(DataType other) { + return types.stream().anyMatch(t -> t.acceptsType(other)); + } + + @Override + public String simpleString() { + return types.stream().map(AbstractDataType::simpleString).collect(Collectors.joining(" or ", "(", ")")); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java index 2291549edf..a59cc989fb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java @@ -18,11 +18,11 @@ package org.apache.doris.nereids.util; import org.apache.doris.nereids.trees.expressions.And; -import org.apache.doris.nereids.trees.expressions.BooleanLiteral; import org.apache.doris.nereids.trees.expressions.CompoundPredicate; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Or; import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java new file mode 100644 index 0000000000..e372e6c3b3 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java @@ -0,0 +1,278 @@ +// 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.util; + +import org.apache.doris.nereids.annotation.Developing; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.types.BigIntType; +import org.apache.doris.nereids.types.BooleanType; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DecimalType; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.FloatType; +import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.types.LargeIntType; +import org.apache.doris.nereids.types.NullType; +import org.apache.doris.nereids.types.SmallIntType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.TinyIntType; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.CharacterType; +import org.apache.doris.nereids.types.coercion.FractionalType; +import org.apache.doris.nereids.types.coercion.IntegralType; +import org.apache.doris.nereids.types.coercion.NumericType; +import org.apache.doris.nereids.types.coercion.PrimitiveType; +import org.apache.doris.nereids.types.coercion.TypeCollection; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Utils for type coercion. + */ +public class TypeCoercionUtils { + + /** + * numeric type precedence for type promotion. + * bigger numeric has smaller ordinal + */ + public static final List NUMERIC_PRECEDENCE = ImmutableList.of( + DoubleType.INSTANCE, + LargeIntType.INSTANCE, + FloatType.INSTANCE, + BigIntType.INSTANCE, + IntegerType.INSTANCE, + SmallIntType.INSTANCE, + TinyIntType.INSTANCE + ); + + /** + * Return Optional.empty() if cannot do implicit cast. + * TODO: datetime and date type + */ + @Developing + public static Optional implicitCast(DataType input, AbstractDataType expected) { + DataType returnType = null; + if (expected.acceptsType(input)) { + // If the expected type is already a parent of the input type, no need to cast. + return Optional.of(input); + } + if (expected instanceof TypeCollection) { + TypeCollection typeCollection = (TypeCollection) expected; + // use origin datatype first. use implicit cast instead if origin type cannot be accepted. + return Stream.>>of( + () -> typeCollection.getTypes().stream() + .filter(e -> e.acceptsType(input)) + .map(e -> input) + .findFirst(), + () -> typeCollection.getTypes().stream() + .map(e -> implicitCast(input, e)) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst()) + .map(Supplier::get) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst(); + } + if (input instanceof NullType) { + // Cast null type (usually from null literals) into target types + returnType = expected.defaultConcreteType(); + } else if (input instanceof NumericType && expected instanceof DecimalType) { + // If input is a numeric type but not decimal, and we expect a decimal type, + // cast the input to decimal. + returnType = DecimalType.forType(input); + } else if (input instanceof NumericType && expected instanceof NumericType) { + // For any other numeric types, implicitly cast to each other, e.g. bigint -> int, int -> bigint + returnType = expected.defaultConcreteType(); + } else if (input instanceof CharacterType) { + if (expected instanceof DecimalType) { + returnType = DecimalType.SYSTEM_DEFAULT; + } else if (expected instanceof NumericType) { + returnType = expected.defaultConcreteType(); + } + } else if (input instanceof PrimitiveType + && expected instanceof CharacterType) { + returnType = StringType.INSTANCE; + } + + // could not do implicit cast, just return null. Throw exception in check analysis. + return Optional.ofNullable(returnType); + } + + /** + * return ture if two type could do type coercion. + */ + public static boolean canHandleTypeCoercion(DataType leftType, DataType rightType) { + if (leftType instanceof DecimalType && rightType instanceof NullType) { + return true; + } + if (leftType instanceof NullType && rightType instanceof DecimalType) { + return true; + } + // TODO: add decimal promotion support + if (!(leftType instanceof DecimalType) && !(rightType instanceof DecimalType) && !leftType.equals(rightType)) { + return true; + } + return false; + } + + /** + * return ture if datatype has character type in it, cannot use instance of CharacterType because of complex type. + */ + @Developing + public static boolean hasCharacterType(DataType dataType) { + // TODO: consider complex type + return dataType instanceof CharacterType; + } + + /** + * find the tightest common type for two type + */ + @Developing + public static Optional findTightestCommonType(DataType left, DataType right) { + // TODO: compatible with origin planner and BE + // TODO: when add new type, add it to here + DataType tightestCommonType = null; + if (left.equals(right)) { + tightestCommonType = left; + } else if (left instanceof NullType) { + tightestCommonType = right; + } else if (right instanceof NullType) { + tightestCommonType = left; + } else if (left instanceof IntegralType && right instanceof DecimalType + && ((DecimalType) right).isWiderThan(left)) { + tightestCommonType = right; + } else if (right instanceof IntegralType && left instanceof DecimalType + && ((DecimalType) left).isWiderThan(right)) { + tightestCommonType = left; + } else if (left instanceof NumericType && right instanceof NumericType + && !(left instanceof DecimalType) && !(right instanceof DecimalType)) { + for (DataType dataType : NUMERIC_PRECEDENCE) { + if (dataType.equals(left) || dataType.equals(right)) { + tightestCommonType = dataType; + break; + } + } + } else if (left instanceof CharacterType || right instanceof CharacterType) { + tightestCommonType = StringType.INSTANCE; + } + return Optional.ofNullable(tightestCommonType); + } + + /** + * find wider common type for data type list. + */ + @Developing + public static Optional findWiderCommonType(List dataTypes) { + // TODO: do not consider complex type + Map> partitioned = dataTypes.stream() + .collect(Collectors.partitioningBy(TypeCoercionUtils::hasCharacterType)); + List needTypeCoercion = Lists.newArrayList(Sets.newHashSet(partitioned.get(true))); + needTypeCoercion.addAll(partitioned.get(false)); + return needTypeCoercion.stream().map(Optional::of).reduce(Optional.of(NullType.INSTANCE), + (r, c) -> { + if (r.isPresent() && c.isPresent()) { + return findWiderTypeForTwo(r.get(), c.get()); + } else { + return Optional.empty(); + } + }); + } + + /** + * find wider common type for two data type. + */ + @Developing + public static Optional findWiderTypeForTwo(DataType left, DataType right) { + // TODO: need to rethink how to handle char and varchar to return char or varchar as much as possible. + return Stream + .>>of( + () -> findTightestCommonType(left, right), + () -> findWiderTypeForDecimal(left, right), + () -> characterPromotion(left, right), + () -> findTypeForComplex(left, right)) + .map(Supplier::get) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst(); + } + + /** + * find wider type for two type that at least one of that is decimal. + */ + @Developing + public static Optional findWiderTypeForDecimal(DataType left, DataType right) { + DataType commonType = null; + if (left instanceof DecimalType && right instanceof DecimalType) { + commonType = DecimalType.widerDecimalType((DecimalType) left, (DecimalType) right); + } else if (left instanceof IntegralType && right instanceof DecimalType) { + commonType = DecimalType.widerDecimalType(DecimalType.forType(left), (DecimalType) right); + } else if (left instanceof DecimalType && right instanceof IntegralType) { + commonType = DecimalType.widerDecimalType((DecimalType) left, DecimalType.forType(right)); + } else if ((left instanceof FractionalType && right instanceof DecimalType) + || (left instanceof DecimalType && right instanceof FractionalType)) { + commonType = DoubleType.INSTANCE; + } + return Optional.ofNullable(commonType); + } + + /** + * do type promotion for two type that at least one of them is CharacterType. + */ + @Developing + public static Optional characterPromotion(DataType left, DataType right) { + // TODO: need to rethink how to handle char and varchar to return char or varchar as much as possible. + if (left instanceof CharacterType && right instanceof PrimitiveType && !(right instanceof BooleanType)) { + return Optional.of(StringType.INSTANCE); + } + if (left instanceof PrimitiveType && !(left instanceof BooleanType) && right instanceof CharacterType) { + return Optional.of(StringType.INSTANCE); + } + return Optional.empty(); + } + + /** + * find common type for complex type. + */ + @Developing + public static Optional findTypeForComplex(DataType left, DataType right) { + // TODO: we need to add real logical here, if we add array type in Nereids + return Optional.empty(); + } + + /** + * cast input type if input's datatype is not same with dateType. + */ + public static Expression castIfNotSameType(Expression input, DataType dataType) { + if (input.getDataType().equals(dataType)) { + return input; + } else { + return new Cast(input, dataType); + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/statistics/SlotStatsDeriveResult.java b/fe/fe-core/src/main/java/org/apache/doris/statistics/SlotStatsDeriveResult.java index c170aa1035..8f5e7e82eb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/statistics/SlotStatsDeriveResult.java +++ b/fe/fe-core/src/main/java/org/apache/doris/statistics/SlotStatsDeriveResult.java @@ -17,7 +17,7 @@ package org.apache.doris.statistics; -import org.apache.doris.nereids.trees.expressions.Literal; +import org.apache.doris.nereids.trees.expressions.literal.Literal; public class SlotStatsDeriveResult { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java index a2c8342b5e..bba08c8a71 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java @@ -18,8 +18,8 @@ package org.apache.doris.nereids.memo; import org.apache.doris.nereids.analyzer.UnboundRelation; -import org.apache.doris.nereids.trees.expressions.BooleanLiteral; import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.JoinType; import org.apache.doris.nereids.trees.plans.Plan; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java new file mode 100644 index 0000000000..f8a38c81e3 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java @@ -0,0 +1,44 @@ +// 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.rules.analysis; + +import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.trees.expressions.And; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.plans.GroupPlan; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; + +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CheckAnalysisTest { + @Mocked + private CascadesContext cascadesContext; + @Mocked + private GroupPlan groupPlan; + + @Test + public void testCheckExpressionInputTypes() { + Plan plan = new LogicalFilter<>(new And(new IntegerLiteral(1), new BooleanLiteral(true)), groupPlan); + CheckAnalysis checkAnalysis = new CheckAnalysis(); + Assertions.assertThrows(RuntimeException.class, () -> checkAnalysis.build().transform(plan, cascadesContext)); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java index 80b42dd8de..1f0196a34e 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java @@ -44,6 +44,7 @@ public class ExpressionRewriteTest { assertRewrite("not x", "not x"); assertRewrite("not not x", "x"); assertRewrite("not not not x", "not x"); + assertRewrite("not not not not x", "x"); assertRewrite("not (x > y)", "x <= y"); assertRewrite("not (x < y)", "x >= y"); assertRewrite("not (x >= y)", "x < y"); @@ -156,13 +157,12 @@ public class ExpressionRewriteTest { // deduplicate assertRewrite("CAST(1 AS int)", "1"); - assertRewrite("CAST(\"str\" AS string)", "\"str\""); + assertRewrite("CAST('str' AS string)", "'str'"); assertRewrite("CAST(CAST(1 AS int) AS int)", "1"); // deduplicate inside - assertRewrite("CAST(CAST(\"str\" AS string) AS double)", "CAST(\"str\" AS double)"); + assertRewrite("CAST(CAST('str' AS string) AS double)", "CAST('str' AS double)"); assertRewrite("CAST(CAST(1 AS int) AS double)", "CAST(1 AS double)"); - } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/TypeCoercionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/TypeCoercionTest.java new file mode 100644 index 0000000000..a4358d56aa --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/TypeCoercionTest.java @@ -0,0 +1,160 @@ +// 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.rules.expression.rewrite; + +import org.apache.doris.nereids.parser.NereidsParser; +import org.apache.doris.nereids.rules.expression.rewrite.rules.TypeCoercion; +import org.apache.doris.nereids.trees.expressions.CaseWhen; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.Divide; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.InPredicate; +import org.apache.doris.nereids.trees.expressions.WhenClause; +import org.apache.doris.nereids.trees.expressions.functions.Avg; +import org.apache.doris.nereids.trees.expressions.functions.Substring; +import org.apache.doris.nereids.trees.expressions.functions.Sum; +import org.apache.doris.nereids.trees.expressions.functions.Year; +import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DateLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; +import org.apache.doris.nereids.types.BigIntType; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.types.StringType; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class TypeCoercionTest { + + private final NereidsParser parser = new NereidsParser(); + private final ExpressionRuleExecutor executor = new ExpressionRuleExecutor(ImmutableList.of(TypeCoercion.INSTANCE)); + + @Test + public void testSubStringImplicitCast() { + Expression expression = new Substring( + new StringLiteral("abc"), + new StringLiteral("1"), + new StringLiteral("3") + ); + Expression expected = new Substring( + new StringLiteral("abc"), + new Cast(new StringLiteral("1"), IntegerType.INSTANCE), + new Cast(new StringLiteral("3"), IntegerType.INSTANCE) + ); + assertRewrite(expected, expression); + } + + @Test + public void testLikeImplicitCast() { + String expression = "1 like 5"; + String expected = "cast(1 as string) like cast(5 as string)"; + assertRewrite(expected, expression); + } + + @Test + public void testRegexImplicitCast() { + String expression = "1 regexp 5"; + String expected = "cast(1 as string) regexp cast(5 as string)"; + assertRewrite(expected, expression); + } + + @Test + public void testYearImplicitCast() { + Expression expression = new Year(new DateLiteral("2022-01-01")); + Expression expected = new Year(new DateLiteral("2022-01-01")); + assertRewrite(expected, expression); + } + + @Test + public void testSumImplicitCast() { + Expression expression = new Sum(new StringLiteral("1")); + Expression expected = new Sum(new Cast(new StringLiteral("1"), DoubleType.INSTANCE)); + assertRewrite(expected, expression); + } + + @Test + public void testAvgImplicitCast() { + Expression expression = new Avg(new StringLiteral("1")); + Expression expected = new Avg(new Cast(new StringLiteral("1"), DoubleType.INSTANCE)); + assertRewrite(expected, expression); + } + + @Test + public void testCaseWhenTypeCoercion() { + WhenClause actualWhenClause1 = new WhenClause(new BooleanLiteral(true), new SmallIntLiteral((short) 1)); + WhenClause actualWhenClause2 = new WhenClause(new BooleanLiteral(true), new DoubleLiteral(1.5)); + List actualWhenClauses = Lists.newArrayList(actualWhenClause1, actualWhenClause2); + Expression actualDefaultValue = new IntegerLiteral(1); + Expression actual = new CaseWhen(actualWhenClauses, actualDefaultValue); + + WhenClause expectedWhenClause1 = new WhenClause(new BooleanLiteral(true), + new Cast(new SmallIntLiteral((short) 1), DoubleType.INSTANCE)); + WhenClause expectedWhenClause2 = new WhenClause(new BooleanLiteral(true), + new DoubleLiteral(1.5)); + List expectedWhenClauses = Lists.newArrayList(expectedWhenClause1, expectedWhenClause2); + Expression expectedDefaultValue = new Cast(new IntegerLiteral(1), DoubleType.INSTANCE); + Expression expected = new CaseWhen(expectedWhenClauses, expectedDefaultValue); + + assertRewrite(expected, actual); + } + + @Test + public void testInPredicate() { + Expression actualCompare = new DoubleLiteral(1.5); + Expression actualOption1 = new StringLiteral("hello"); + Expression actualOption2 = new IntegerLiteral(1); + List actualOptions = Lists.newArrayList(actualOption1, actualOption2); + Expression actual = new InPredicate(actualCompare, actualOptions); + + Expression expectedCompare = new Cast(new DoubleLiteral(1.5), StringType.INSTANCE); + Expression expectedOption1 = new StringLiteral("hello"); + Expression expectedOption2 = new Cast(new IntegerLiteral(1), StringType.INSTANCE); + List expectedOptions = Lists.newArrayList(expectedOption1, expectedOption2); + Expression expected = new InPredicate(expectedCompare, expectedOptions); + + assertRewrite(expected, actual); + } + + @Test + public void testBinaryOperator() { + Expression actual = new Divide(new SmallIntLiteral((short) 1), new BigIntLiteral(10L)); + Expression expected = new Divide(new Cast(new SmallIntLiteral((short) 1), BigIntType.INSTANCE), + new BigIntLiteral(10L)); + assertRewrite(expected, actual); + } + + private void assertRewrite(Expression expected, Expression expression) { + Expression rewrittenExpression = executor.rewrite(expression); + Assertions.assertEquals(expected, rewrittenExpression); + } + + private void assertRewrite(String expected, String expression) { + Expression needRewriteExpression = parser.parseExpression(expression); + Expression expectedExpression = parser.parseExpression(expected); + assertRewrite(expectedExpression, needRewriteExpression); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java index 12a4083a76..6d2dbc6d8a 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java @@ -21,10 +21,10 @@ import org.apache.doris.nereids.rules.rewrite.AggregateDisassemble; import org.apache.doris.nereids.trees.expressions.Add; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.expressions.functions.Sum; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.AggPhase; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/JoinReorderTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/JoinReorderTest.java index 95b4ea781c..00c37f4e8c 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/JoinReorderTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/JoinReorderTest.java @@ -18,7 +18,7 @@ package org.apache.doris.nereids.rules.rewrite.logical; import org.apache.doris.nereids.analyzer.UnboundRelation; -import org.apache.doris.nereids.trees.expressions.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import org.apache.doris.nereids.trees.plans.JoinType; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveFilterTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveFilterTest.java index 332fac8a56..43aa97dcea 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveFilterTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveFilterTest.java @@ -21,7 +21,7 @@ import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.analyzer.UnboundRelation; import org.apache.doris.nereids.rules.Rule; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; import org.apache.doris.nereids.util.ExpressionUtils; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveProjectsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveProjectsTest.java index 83b08d48db..52bf4f55ef 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveProjectsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeConsecutiveProjectsTest.java @@ -22,10 +22,10 @@ import org.apache.doris.nereids.analyzer.UnboundRelation; import org.apache.doris.nereids.rules.Rule; import org.apache.doris.nereids.trees.expressions.Add; import org.apache.doris.nereids.trees.expressions.Alias; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalProject; import org.apache.doris.nereids.types.IntegerType; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java index 9c550a4929..ae4b24da34 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java @@ -23,19 +23,22 @@ import org.apache.doris.nereids.rules.expression.rewrite.ExpressionNormalization import org.apache.doris.nereids.trees.expressions.Add; import org.apache.doris.nereids.trees.expressions.And; import org.apache.doris.nereids.trees.expressions.Between; +import org.apache.doris.nereids.trees.expressions.Cast; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.GreaterThan; import org.apache.doris.nereids.trees.expressions.GreaterThanEqual; import org.apache.doris.nereids.trees.expressions.LessThanEqual; -import org.apache.doris.nereids.trees.expressions.Literal; import org.apache.doris.nereids.trees.expressions.Subtract; +import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.plans.JoinType; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan; import org.apache.doris.nereids.trees.plans.logical.LogicalProject; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.StringType; import org.apache.doris.nereids.util.ExpressionUtils; import org.apache.doris.nereids.util.PlanConstructor; import org.apache.doris.nereids.util.PlanRewriter; @@ -56,8 +59,6 @@ import java.util.Optional; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class PushDownPredicateTest { - - private Plan rStudent; private Plan rScore; private Plan rCourse; @@ -96,12 +97,9 @@ public class PushDownPredicateTest { filter ); - System.out.println(root.treeString()); - Memo memo = rewrite(root); Group rootGroup = memo.getRoot(); - System.out.println(memo.copyOut().treeString()); Plan op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getPlan(); Plan op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression() @@ -118,7 +116,9 @@ public class PushDownPredicateTest { Assertions.assertEquals(onCondition1, join1.getCondition().get()); Assertions.assertEquals(ExpressionUtils.and(onCondition2, whereCondition1), filter1.getPredicates()); - Assertions.assertEquals(ExpressionUtils.and(onCondition3, whereCondition2), filter2.getPredicates()); + Assertions.assertEquals(ExpressionUtils.and(onCondition3, + new GreaterThan(rScore.getOutput().get(2), new Cast(Literal.of(60), DoubleType.INSTANCE))), + filter2.getPredicates()); } @Test @@ -139,11 +139,8 @@ public class PushDownPredicateTest { filter ); - System.out.println(root.treeString()); - Memo memo = rewrite(root); Group rootGroup = memo.getRoot(); - System.out.println(memo.copyOut().treeString()); Plan op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getPlan(); Plan op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression() @@ -159,7 +156,9 @@ public class PushDownPredicateTest { LogicalFilter filter2 = (LogicalFilter) op3; Assertions.assertEquals(whereCondition1, join1.getCondition().get()); Assertions.assertEquals(whereCondition2, filter1.getPredicates()); - Assertions.assertEquals(whereCondition3, filter2.getPredicates()); + Assertions.assertEquals( + new GreaterThan(rScore.getOutput().get(2), new Cast(Literal.of(60), DoubleType.INSTANCE)), + filter2.getPredicates()); } @Test @@ -181,8 +180,8 @@ public class PushDownPredicateTest { Expression whereCondition3 = new Between(rStudent.getOutput().get(2), Literal.of(18), Literal.of(20)); // student.age >= 18 and student.age <= 20 Expression whereCondition3result = new And( - new GreaterThanEqual(rStudent.getOutput().get(2), Literal.of(18)), - new LessThanEqual(rStudent.getOutput().get(2), Literal.of(20))); + new GreaterThanEqual(rStudent.getOutput().get(2), new Cast(Literal.of(18), StringType.INSTANCE)), + new LessThanEqual(rStudent.getOutput().get(2), new Cast(Literal.of(20), StringType.INSTANCE))); // score.grade > 60 Expression whereCondition4 = new GreaterThan(rScore.getOutput().get(2), Literal.of(60)); @@ -198,11 +197,9 @@ public class PushDownPredicateTest { Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2)), filter ); - System.out.println(root.treeString()); Memo memo = rewrite(root); Group rootGroup = memo.getRoot(); - System.out.println(memo.copyOut().treeString()); Plan join2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getPlan(); Plan join3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression() .getPlan(); @@ -219,7 +216,9 @@ public class PushDownPredicateTest { Assertions.assertEquals(whereCondition2, ((LogicalJoin) join2).getCondition().get()); Assertions.assertEquals(whereCondition1, ((LogicalJoin) join3).getCondition().get()); Assertions.assertEquals(whereCondition3result.toSql(), ((LogicalFilter) op1).getPredicates().toSql()); - Assertions.assertEquals(whereCondition4, ((LogicalFilter) op2).getPredicates()); + Assertions.assertEquals( + new GreaterThan(rScore.getOutput().get(2), new Cast(Literal.of(60), DoubleType.INSTANCE)), + ((LogicalFilter) op2).getPredicates()); } private Memo rewrite(Plan plan) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateThroughAggregationTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateThroughAggregationTest.java index 39667fa90f..fe762357b0 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateThroughAggregationTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateThroughAggregationTest.java @@ -27,9 +27,9 @@ import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.GreaterThan; import org.apache.doris.nereids.trees.expressions.LessThanEqual; -import org.apache.doris.nereids.trees.expressions.Literal; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java index 67045678aa..5bba0266a4 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java @@ -26,12 +26,12 @@ import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.And; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.Or; import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction; import org.apache.doris.nereids.trees.expressions.functions.Sum; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.JoinType; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpectedInputTypesTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpectedInputTypesTest.java new file mode 100644 index 0000000000..340d5c79df --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpectedInputTypesTest.java @@ -0,0 +1,505 @@ +// 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.trees.expressions; + +import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.CharLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral; +import org.apache.doris.nereids.trees.expressions.literal.FloatLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; +import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; +import org.apache.doris.nereids.trees.expressions.typecoercion.TypeCheckResult; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; + +public class ExpectedInputTypesTest { + @Test + public void testAnd() { + TypeCheckResult typeCheckResult; + + And secondInputIsNotBoolean = new And(new IntegerLiteral(1), new BooleanLiteral(true)); + typeCheckResult = secondInputIsNotBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + + And firstInputIsNotBoolean = new And(new BooleanLiteral(true), new IntegerLiteral(1)); + typeCheckResult = firstInputIsNotBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + + And bothInputAreBoolean = new And(new BooleanLiteral(true), new BooleanLiteral(false)); + typeCheckResult = bothInputAreBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + } + + @Test + public void testOr() { + TypeCheckResult typeCheckResult; + + Or secondInputIsNotBoolean = new Or(new IntegerLiteral(1), new BooleanLiteral(true)); + typeCheckResult = secondInputIsNotBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + + Or firstInputIsNotBoolean = new Or(new BooleanLiteral(true), new IntegerLiteral(1)); + typeCheckResult = firstInputIsNotBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + + Or bothInputAreBoolean = new Or(new BooleanLiteral(true), new BooleanLiteral(false)); + typeCheckResult = bothInputAreBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + } + + @Test + public void testDivide() { + TypeCheckResult typeCheckResult; + + Divide tinyInt = new Divide(new TinyIntLiteral((byte) 1), new TinyIntLiteral((byte) 2)); + typeCheckResult = tinyInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide smallInt = new Divide(new SmallIntLiteral((short) 1), new SmallIntLiteral((short) 2)); + typeCheckResult = smallInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide integer = new Divide(new IntegerLiteral(1), new IntegerLiteral(2)); + typeCheckResult = integer.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide bigint = new Divide(new BigIntLiteral(1L), new BigIntLiteral(2L)); + typeCheckResult = bigint.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide largeInt = new Divide(new LargeIntLiteral(new BigInteger("1")), + new LargeIntLiteral(new BigInteger("1"))); + typeCheckResult = largeInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide floatType = new Divide(new FloatLiteral(1.0F), new FloatLiteral(2.0F)); + typeCheckResult = floatType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide doubleType = new Divide(new DoubleLiteral(1.0), new DoubleLiteral(2.0)); + typeCheckResult = doubleType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide decimalType = new Divide(new DecimalLiteral(new BigDecimal("12.3")), + new DecimalLiteral(new BigDecimal("12.3"))); + typeCheckResult = decimalType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Divide charType = new Divide(new CharLiteral("1", 1), new CharLiteral("2", 1)); + typeCheckResult = charType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Divide varchar = new Divide(new VarcharLiteral("1", 10), new VarcharLiteral("2", 10)); + typeCheckResult = varchar.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Divide string = new Divide(new StringLiteral("1"), new StringLiteral("2")); + typeCheckResult = string.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Divide bool = new Divide(new BooleanLiteral(false), new BooleanLiteral(true)); + typeCheckResult = bool.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Divide nullType = new Divide(NullLiteral.INSTANCE, NullLiteral.INSTANCE); + typeCheckResult = nullType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + } + + @Test + public void testComparisonPredicate() { + TypeCheckResult typeCheckResult; + + LessThan tinyInt = new LessThan(new TinyIntLiteral((byte) 1), new TinyIntLiteral((byte) 2)); + typeCheckResult = tinyInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan smallInt = new LessThan(new SmallIntLiteral((short) 1), new SmallIntLiteral((short) 2)); + typeCheckResult = smallInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan integer = new LessThan(new IntegerLiteral(1), new IntegerLiteral(2)); + typeCheckResult = integer.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan bigint = new LessThan(new BigIntLiteral(1L), new BigIntLiteral(2L)); + typeCheckResult = bigint.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan largeInt = new LessThan(new LargeIntLiteral(new BigInteger("1")), + new LargeIntLiteral(new BigInteger("1"))); + typeCheckResult = largeInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan floatType = new LessThan(new FloatLiteral(1.0F), new FloatLiteral(2.0F)); + typeCheckResult = floatType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan doubleType = new LessThan(new DoubleLiteral(1.0), new DoubleLiteral(2.0)); + typeCheckResult = doubleType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan decimalType = new LessThan(new DecimalLiteral(new BigDecimal("12.3")), + new DecimalLiteral(new BigDecimal("12.3"))); + typeCheckResult = decimalType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan charType = new LessThan(new CharLiteral("1", 1), new CharLiteral("2", 1)); + typeCheckResult = charType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan varchar = new LessThan(new VarcharLiteral("1", 10), new VarcharLiteral("2", 10)); + typeCheckResult = varchar.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan string = new LessThan(new StringLiteral("1"), new StringLiteral("2")); + typeCheckResult = string.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan bool = new LessThan(new BooleanLiteral(false), new BooleanLiteral(true)); + typeCheckResult = bool.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + LessThan nullType = new LessThan(NullLiteral.INSTANCE, NullLiteral.INSTANCE); + typeCheckResult = nullType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + } + + @Test + public void testMultiply() { + TypeCheckResult typeCheckResult; + + Multiply tinyInt = new Multiply(new TinyIntLiteral((byte) 1), new TinyIntLiteral((byte) 2)); + typeCheckResult = tinyInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply smallInt = new Multiply(new SmallIntLiteral((short) 1), new SmallIntLiteral((short) 2)); + typeCheckResult = smallInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply integer = new Multiply(new IntegerLiteral(1), new IntegerLiteral(2)); + typeCheckResult = integer.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply bigint = new Multiply(new BigIntLiteral(1L), new BigIntLiteral(2L)); + typeCheckResult = bigint.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply largeInt = new Multiply(new LargeIntLiteral(new BigInteger("1")), + new LargeIntLiteral(new BigInteger("1"))); + typeCheckResult = largeInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply floatType = new Multiply(new FloatLiteral(1.0F), new FloatLiteral(2.0F)); + typeCheckResult = floatType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply doubleType = new Multiply(new DoubleLiteral(1.0), new DoubleLiteral(2.0)); + typeCheckResult = doubleType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply decimalType = new Multiply(new DecimalLiteral(new BigDecimal("12.3")), + new DecimalLiteral(new BigDecimal("12.3"))); + typeCheckResult = decimalType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Multiply charType = new Multiply(new CharLiteral("1", 1), new CharLiteral("2", 1)); + typeCheckResult = charType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Multiply varchar = new Multiply(new VarcharLiteral("1", 10), new VarcharLiteral("2", 10)); + typeCheckResult = varchar.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Multiply string = new Multiply(new StringLiteral("1"), new StringLiteral("2")); + typeCheckResult = string.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Multiply bool = new Multiply(new BooleanLiteral(false), new BooleanLiteral(true)); + typeCheckResult = bool.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Multiply nullType = new Multiply(NullLiteral.INSTANCE, NullLiteral.INSTANCE); + typeCheckResult = nullType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + } + + @Test + public void testSubtract() { + TypeCheckResult typeCheckResult; + + Subtract tinyInt = new Subtract(new TinyIntLiteral((byte) 1), new TinyIntLiteral((byte) 2)); + typeCheckResult = tinyInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract smallInt = new Subtract(new SmallIntLiteral((short) 1), new SmallIntLiteral((short) 2)); + typeCheckResult = smallInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract integer = new Subtract(new IntegerLiteral(1), new IntegerLiteral(2)); + typeCheckResult = integer.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract bigint = new Subtract(new BigIntLiteral(1L), new BigIntLiteral(2L)); + typeCheckResult = bigint.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract largeInt = new Subtract(new LargeIntLiteral(new BigInteger("1")), + new LargeIntLiteral(new BigInteger("1"))); + typeCheckResult = largeInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract floatType = new Subtract(new FloatLiteral(1.0F), new FloatLiteral(2.0F)); + typeCheckResult = floatType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract doubleType = new Subtract(new DoubleLiteral(1.0), new DoubleLiteral(2.0)); + typeCheckResult = doubleType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract decimalType = new Subtract(new DecimalLiteral(new BigDecimal("12.3")), + new DecimalLiteral(new BigDecimal("12.3"))); + typeCheckResult = decimalType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Subtract charType = new Subtract(new CharLiteral("1", 1), new CharLiteral("2", 1)); + typeCheckResult = charType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Subtract varchar = new Subtract(new VarcharLiteral("1", 10), new VarcharLiteral("2", 10)); + typeCheckResult = varchar.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Subtract string = new Subtract(new StringLiteral("1"), new StringLiteral("2")); + typeCheckResult = string.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Subtract bool = new Subtract(new BooleanLiteral(false), new BooleanLiteral(true)); + typeCheckResult = bool.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Subtract nullType = new Subtract(NullLiteral.INSTANCE, NullLiteral.INSTANCE); + typeCheckResult = nullType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + } + + @Test + public void testMod() { + TypeCheckResult typeCheckResult; + + Mod tinyInt = new Mod(new TinyIntLiteral((byte) 1), new TinyIntLiteral((byte) 2)); + typeCheckResult = tinyInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod smallInt = new Mod(new SmallIntLiteral((short) 1), new SmallIntLiteral((short) 2)); + typeCheckResult = smallInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod integer = new Mod(new IntegerLiteral(1), new IntegerLiteral(2)); + typeCheckResult = integer.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod bigint = new Mod(new BigIntLiteral(1L), new BigIntLiteral(2L)); + typeCheckResult = bigint.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod largeInt = new Mod(new LargeIntLiteral(new BigInteger("1")), + new LargeIntLiteral(new BigInteger("1"))); + typeCheckResult = largeInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod floatType = new Mod(new FloatLiteral(1.0F), new FloatLiteral(2.0F)); + typeCheckResult = floatType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod doubleType = new Mod(new DoubleLiteral(1.0), new DoubleLiteral(2.0)); + typeCheckResult = doubleType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod decimalType = new Mod(new DecimalLiteral(new BigDecimal("12.3")), + new DecimalLiteral(new BigDecimal("12.3"))); + typeCheckResult = decimalType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Mod charType = new Mod(new CharLiteral("1", 1), new CharLiteral("2", 1)); + typeCheckResult = charType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Mod varchar = new Mod(new VarcharLiteral("1", 10), new VarcharLiteral("2", 10)); + typeCheckResult = varchar.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Mod string = new Mod(new StringLiteral("1"), new StringLiteral("2")); + typeCheckResult = string.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Mod bool = new Mod(new BooleanLiteral(false), new BooleanLiteral(true)); + typeCheckResult = bool.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Mod nullType = new Mod(NullLiteral.INSTANCE, NullLiteral.INSTANCE); + typeCheckResult = nullType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + } + + @Test + public void testAdd() { + TypeCheckResult typeCheckResult; + + Add tinyInt = new Add(new TinyIntLiteral((byte) 1), new TinyIntLiteral((byte) 2)); + typeCheckResult = tinyInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add smallInt = new Add(new SmallIntLiteral((short) 1), new SmallIntLiteral((short) 2)); + typeCheckResult = smallInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add integer = new Add(new IntegerLiteral(1), new IntegerLiteral(2)); + typeCheckResult = integer.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add bigint = new Add(new BigIntLiteral(1L), new BigIntLiteral(2L)); + typeCheckResult = bigint.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add largeInt = new Add(new LargeIntLiteral(new BigInteger("1")), + new LargeIntLiteral(new BigInteger("1"))); + typeCheckResult = largeInt.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add floatType = new Add(new FloatLiteral(1.0F), new FloatLiteral(2.0F)); + typeCheckResult = floatType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add doubleType = new Add(new DoubleLiteral(1.0), new DoubleLiteral(2.0)); + typeCheckResult = doubleType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add decimalType = new Add(new DecimalLiteral(new BigDecimal("12.3")), + new DecimalLiteral(new BigDecimal("12.3"))); + typeCheckResult = decimalType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + + Add charType = new Add(new CharLiteral("1", 1), new CharLiteral("2", 1)); + typeCheckResult = charType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Add varchar = new Add(new VarcharLiteral("1", 10), new VarcharLiteral("2", 10)); + typeCheckResult = varchar.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Add string = new Add(new StringLiteral("1"), new StringLiteral("2")); + typeCheckResult = string.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Add bool = new Add(new BooleanLiteral(false), new BooleanLiteral(true)); + typeCheckResult = bool.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + + Add nullType = new Add(NullLiteral.INSTANCE, NullLiteral.INSTANCE); + typeCheckResult = nullType.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + // this means has two type check error + Assertions.assertEquals(4, typeCheckResult.getMessage().split(",").length); + } + + @Test + public void testNot() { + TypeCheckResult typeCheckResult; + + Not childIsNotBoolean = new Not(new IntegerLiteral(1)); + typeCheckResult = childIsNotBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + + Not childIsBoolean = new Not(new BooleanLiteral(true)); + typeCheckResult = childIsBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + } + + @Test + public void testWhenClause() { + TypeCheckResult typeCheckResult; + + WhenClause firstInputIsNotBoolean = new WhenClause(new IntegerLiteral(1), new BooleanLiteral(true)); + typeCheckResult = firstInputIsNotBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.failed()); + + WhenClause firstInputIsBoolean = new WhenClause(new BooleanLiteral(true), new BooleanLiteral(true)); + typeCheckResult = firstInputIsBoolean.checkInputDataTypes(); + Assertions.assertTrue(typeCheckResult.success()); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionTest.java index c2d7e2b348..d673cfa9a0 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionTest.java @@ -17,6 +17,8 @@ package org.apache.doris.nereids.trees.expressions; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; import org.apache.doris.nereids.types.IntegerType; import org.junit.jupiter.api.Assertions; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java index fe128534ee..2aa3c5864a 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/LiteralTest.java @@ -18,6 +18,12 @@ package org.apache.doris.nereids.trees.expressions; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java index 36b79e57f2..597c4c24d9 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java @@ -23,9 +23,9 @@ import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.properties.OrderKey; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.Literal; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java new file mode 100644 index 0000000000..9854086e22 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/AbstractDataTypeTest.java @@ -0,0 +1,540 @@ +// 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.types; + +import org.apache.doris.nereids.types.coercion.AnyDataType; +import org.apache.doris.nereids.types.coercion.CharacterType; +import org.apache.doris.nereids.types.coercion.FractionalType; +import org.apache.doris.nereids.types.coercion.IntegralType; +import org.apache.doris.nereids.types.coercion.NumericType; +import org.apache.doris.nereids.types.coercion.TypeCollection; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Random; + +public class AbstractDataTypeTest { + @Test + public void testAnyAccept() { + AnyDataType dateType = AnyDataType.INSTANCE; + Assertions.assertTrue(dateType.acceptsType(NullType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(FloatType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertTrue(dateType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertTrue(dateType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertTrue(dateType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertTrue(dateType.acceptsType(StringType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(DateType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testNullTypeAccept() { + NullType dateType = NullType.INSTANCE; + Assertions.assertTrue(dateType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dateType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dateType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dateType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dateType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testBooleanAccept() { + BooleanType dateType = BooleanType.INSTANCE; + Assertions.assertFalse(dateType.acceptsType(NullType.INSTANCE)); + Assertions.assertTrue(dateType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dateType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dateType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dateType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dateType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dateType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testNumericAccept() { + NumericType dataType = NumericType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertTrue(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testIntegralAccept() { + IntegralType dataType = IntegralType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testTinyIntAccept() { + TinyIntType dataType = TinyIntType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testSmallIntAccept() { + SmallIntType dataType = SmallIntType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testIntAccept() { + IntegerType dataType = IntegerType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testBigIntAccept() { + BigIntType dataType = BigIntType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testLargeIntAccept() { + LargeIntType dataType = LargeIntType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testFractionalAccept() { + FractionalType dataType = FractionalType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertTrue(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testFloatAccept() { + FloatType dataType = FloatType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testDoubleAccept() { + DoubleType dataType = DoubleType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testDecimalAccept() { + DecimalType dataType = DecimalType.SYSTEM_DEFAULT; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertTrue(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testCharacterAccept() { + CharacterType dataType = CharacterType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertTrue(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertTrue(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertTrue(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testCharAccept() { + CharType dataType = CharType.createCharType(10); + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertTrue(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testVarcharAccept() { + VarcharType dataType = VarcharType.SYSTEM_DEFAULT; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertTrue(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testStringAccept() { + StringType dataType = StringType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertTrue(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testDateAccept() { + DateType dataType = DateType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testDateTimeAccept() { + DateTimeType dataType = DateTimeType.INSTANCE; + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testTypeCollectionAccept() { + TypeCollection dataType = new TypeCollection(SmallIntType.INSTANCE, DateTimeType.INSTANCE); + Assertions.assertFalse(dataType.acceptsType(NullType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BooleanType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(TinyIntType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(SmallIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(IntegerType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(BigIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(LargeIntType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(FloatType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DoubleType.INSTANCE)); + int precision = Math.abs(new Random().nextInt() % (DecimalType.MAX_PRECISION - 1)) + 1; + int scale = Math.min(precision, Math.abs(new Random().nextInt() % DecimalType.MAX_SCALE)); + Assertions.assertFalse(dataType.acceptsType(new DecimalType(precision, scale))); + Assertions.assertFalse(dataType.acceptsType(new CharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(new VarcharType(new Random().nextInt()))); + Assertions.assertFalse(dataType.acceptsType(StringType.INSTANCE)); + Assertions.assertFalse(dataType.acceptsType(DateType.INSTANCE)); + Assertions.assertTrue(dataType.acceptsType(DateTimeType.INSTANCE)); + } + + @Test + public void testSimpleName() { + Assertions.assertEquals("null", NullType.INSTANCE.simpleString()); + Assertions.assertEquals("boolean", BooleanType.INSTANCE.simpleString()); + Assertions.assertEquals("numeric", NumericType.INSTANCE.simpleString()); + Assertions.assertEquals("integral", IntegralType.INSTANCE.simpleString()); + Assertions.assertEquals("tinyint", TinyIntType.INSTANCE.simpleString()); + Assertions.assertEquals("smallint", SmallIntType.INSTANCE.simpleString()); + Assertions.assertEquals("int", IntegerType.INSTANCE.simpleString()); + Assertions.assertEquals("bigint", BigIntType.INSTANCE.simpleString()); + Assertions.assertEquals("largeint", LargeIntType.INSTANCE.simpleString()); + Assertions.assertEquals("fractional", FractionalType.INSTANCE.simpleString()); + Assertions.assertEquals("float", FloatType.INSTANCE.simpleString()); + Assertions.assertEquals("double", DoubleType.INSTANCE.simpleString()); + Assertions.assertEquals("decimal", DecimalType.SYSTEM_DEFAULT.simpleString()); + Assertions.assertEquals("character", CharacterType.INSTANCE.simpleString()); + Assertions.assertEquals("char", new CharType(10).simpleString()); + Assertions.assertEquals("varchar", VarcharType.SYSTEM_DEFAULT.simpleString()); + Assertions.assertEquals("string", StringType.INSTANCE.simpleString()); + Assertions.assertEquals("date", DateType.INSTANCE.simpleString()); + Assertions.assertEquals("datetime", DateTimeType.INSTANCE.simpleString()); + } + + @Test + public void testDefaultConcreteType() { + Assertions.assertEquals(NullType.INSTANCE, NullType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(BooleanType.INSTANCE, BooleanType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(DoubleType.INSTANCE, NumericType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(BigIntType.INSTANCE, IntegralType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(TinyIntType.INSTANCE, TinyIntType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(SmallIntType.INSTANCE, SmallIntType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(IntegerType.INSTANCE, IntegerType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(BigIntType.INSTANCE, BigIntType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(LargeIntType.INSTANCE, LargeIntType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(DoubleType.INSTANCE, FractionalType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(FloatType.INSTANCE, FloatType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(DoubleType.INSTANCE, DoubleType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(DecimalType.SYSTEM_DEFAULT, DecimalType.SYSTEM_DEFAULT.defaultConcreteType()); + Assertions.assertEquals(StringType.INSTANCE, CharacterType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(new CharType(10), new CharType(10).defaultConcreteType()); + Assertions.assertEquals(VarcharType.SYSTEM_DEFAULT, VarcharType.SYSTEM_DEFAULT.defaultConcreteType()); + Assertions.assertEquals(StringType.INSTANCE, StringType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(DateType.INSTANCE, DateType.INSTANCE.defaultConcreteType()); + Assertions.assertEquals(DateTimeType.INSTANCE, DateTimeType.INSTANCE.defaultConcreteType()); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java index 93e8e4d164..3afad0deb2 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java @@ -24,42 +24,91 @@ import org.junit.jupiter.api.Test; public class DataTypeTest { @Test public void testDataTypeEquals() { - BigIntType bigIntType1 = new BigIntType(); - BigIntType bigIntType2 = new BigIntType(); - Assertions.assertEquals(bigIntType1, bigIntType2); - Assertions.assertEquals(bigIntType1.hashCode(), bigIntType2.hashCode()); + NullType nullType1 = new NullType(); + NullType nullType2 = new NullType(); + Assertions.assertEquals(nullType1, nullType2); + Assertions.assertEquals(nullType1.hashCode(), nullType2.hashCode()); BooleanType booleanType1 = new BooleanType(); BooleanType booleanType2 = new BooleanType(); Assertions.assertEquals(booleanType1, booleanType2); Assertions.assertEquals(booleanType1.hashCode(), booleanType2.hashCode()); - DoubleType doubleType1 = new DoubleType(); - DoubleType doubleType2 = new DoubleType(); - Assertions.assertEquals(doubleType1, doubleType2); - Assertions.assertEquals(doubleType1.hashCode(), doubleType2.hashCode()); + TinyIntType tinyIntType1 = new TinyIntType(); + TinyIntType tinyIntType2 = new TinyIntType(); + Assertions.assertEquals(tinyIntType1, tinyIntType2); + Assertions.assertEquals(tinyIntType1.hashCode(), tinyIntType2.hashCode()); + + SmallIntType smallIntType1 = new SmallIntType(); + SmallIntType smallIntType2 = new SmallIntType(); + Assertions.assertEquals(smallIntType1, smallIntType2); + Assertions.assertEquals(smallIntType1.hashCode(), smallIntType2.hashCode()); IntegerType integerType1 = new IntegerType(); IntegerType integerType2 = new IntegerType(); Assertions.assertEquals(integerType1, integerType2); Assertions.assertEquals(integerType1.hashCode(), integerType2.hashCode()); - NullType nullType1 = new NullType(); - NullType nullType2 = new NullType(); - Assertions.assertEquals(nullType1, nullType2); - Assertions.assertEquals(nullType1.hashCode(), nullType2.hashCode()); + BigIntType bigIntType1 = new BigIntType(); + BigIntType bigIntType2 = new BigIntType(); + Assertions.assertEquals(bigIntType1, bigIntType2); + Assertions.assertEquals(bigIntType1.hashCode(), bigIntType2.hashCode()); + + LargeIntType largeIntType1 = new LargeIntType(); + LargeIntType largeIntType2 = new LargeIntType(); + Assertions.assertEquals(largeIntType1, largeIntType2); + Assertions.assertEquals(largeIntType1.hashCode(), largeIntType2.hashCode()); + + FloatType floatType1 = new FloatType(); + FloatType floatType2 = new FloatType(); + Assertions.assertEquals(floatType1, floatType2); + Assertions.assertEquals(floatType1.hashCode(), floatType2.hashCode()); + + DoubleType doubleType1 = new DoubleType(); + DoubleType doubleType2 = new DoubleType(); + Assertions.assertEquals(doubleType1, doubleType2); + Assertions.assertEquals(doubleType1.hashCode(), doubleType2.hashCode()); + + DecimalType decimalType1 = new DecimalType(27, 9); + DecimalType decimalType2 = new DecimalType(27, 9); + DecimalType decimalType3 = new DecimalType(28, 9); + DecimalType decimalType4 = new DecimalType(27, 10); + Assertions.assertEquals(decimalType1, decimalType2); + Assertions.assertEquals(decimalType1.hashCode(), decimalType2.hashCode()); + Assertions.assertNotEquals(decimalType1, decimalType3); + Assertions.assertNotEquals(decimalType1.hashCode(), decimalType3.hashCode()); + Assertions.assertNotEquals(decimalType1, decimalType4); + Assertions.assertNotEquals(decimalType1.hashCode(), decimalType4.hashCode()); + + CharType charType1 = new CharType(10); + CharType charType2 = new CharType(10); + CharType charType3 = new CharType(15); + Assertions.assertEquals(charType1, charType2); + Assertions.assertEquals(charType1.hashCode(), charType2.hashCode()); + Assertions.assertNotEquals(charType1, charType3); + Assertions.assertNotEquals(charType1.hashCode(), charType3.hashCode()); + + VarcharType varcharType1 = new VarcharType(32); + VarcharType varcharType2 = new VarcharType(32); + VarcharType varcharType3 = new VarcharType(64); + Assertions.assertEquals(varcharType1, varcharType2); + Assertions.assertEquals(varcharType1.hashCode(), varcharType2.hashCode()); + Assertions.assertNotEquals(varcharType1, varcharType3); + Assertions.assertNotEquals(varcharType1.hashCode(), varcharType3.hashCode()); StringType stringType1 = new StringType(); StringType stringType2 = new StringType(); Assertions.assertEquals(stringType1, stringType2); Assertions.assertEquals(stringType1.hashCode(), stringType2.hashCode()); - VarcharType varcharType1 = new VarcharType(32); - VarcharType varcharType2 = new VarcharType(32); - Assertions.assertEquals(varcharType1, varcharType2); - Assertions.assertEquals(varcharType1.hashCode(), varcharType2.hashCode()); - VarcharType varcharType3 = new VarcharType(64); - Assertions.assertNotEquals(varcharType1, varcharType3); - Assertions.assertNotEquals(varcharType1.hashCode(), varcharType3.hashCode()); + DateType dateType1 = new DateType(); + DateType dateType2 = new DateType(); + Assertions.assertEquals(dateType1, dateType2); + Assertions.assertEquals(dateType1.hashCode(), dateType2.hashCode()); + + DateTimeType dateTimeType1 = new DateTimeType(); + DateTimeType dateTimeType2 = new DateTimeType(); + Assertions.assertEquals(dateTimeType1, dateTimeType2); + Assertions.assertEquals(dateTimeType1.hashCode(), dateTimeType2.hashCode()); } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DecimalTypeTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DecimalTypeTest.java new file mode 100644 index 0000000000..b22113724a --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DecimalTypeTest.java @@ -0,0 +1,47 @@ +// 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.types; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class DecimalTypeTest { + @Test + public void testIsWiderThan() { + Assertions.assertTrue(DecimalType.createDecimalType(10, 5) + .isWiderThan(DecimalType.createDecimalType(8, 4))); + Assertions.assertFalse(DecimalType.createDecimalType(10, 5) + .isWiderThan(DecimalType.createDecimalType(8, 6))); + Assertions.assertFalse(DecimalType.createDecimalType(10, 5) + .isWiderThan(DecimalType.createDecimalType(8, 2))); + } + + @Test + public void testWiderDecimal() { + Assertions.assertEquals(DecimalType.createDecimalType(14, 5), + DecimalType.widerDecimalType( + DecimalType.createDecimalType(12, 3), + DecimalType.createDecimalType(10, 5))); + } + + @Test + public void testConstructor() { + Assertions.assertEquals(DecimalType.createDecimalType(38, 38), + DecimalType.createDecimalType(39, 39)); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java new file mode 100644 index 0000000000..92613e4b86 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java @@ -0,0 +1,199 @@ +// 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.util; + +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral; +import org.apache.doris.nereids.types.BigIntType; +import org.apache.doris.nereids.types.BooleanType; +import org.apache.doris.nereids.types.CharType; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.nereids.types.DateTimeType; +import org.apache.doris.nereids.types.DateType; +import org.apache.doris.nereids.types.DecimalType; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.FloatType; +import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.types.LargeIntType; +import org.apache.doris.nereids.types.NullType; +import org.apache.doris.nereids.types.SmallIntType; +import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.TinyIntType; +import org.apache.doris.nereids.types.VarcharType; +import org.apache.doris.nereids.types.coercion.IntegralType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +public class TypeCoercionUtilsTest { + @Test + public void testImplicitCastAccept() { + IntegerType integerType = IntegerType.INSTANCE; + IntegralType integralType = IntegralType.INSTANCE; + Assertions.assertEquals(integerType, TypeCoercionUtils.implicitCast(integerType, integralType).get()); + } + + @Test + public void testImplicitCastNullType() { + NullType nullType = NullType.INSTANCE; + IntegralType integralType = IntegralType.INSTANCE; + Assertions.assertEquals(integralType.defaultConcreteType(), + TypeCoercionUtils.implicitCast(nullType, integralType).get()); + } + + @Test + public void testImplicitCastNumericWithExpectDecimal() { + BigIntType bigIntType = BigIntType.INSTANCE; + DecimalType decimalType = DecimalType.createDecimalType(27, 9); + Assertions.assertEquals(DecimalType.forType(bigIntType), + TypeCoercionUtils.implicitCast(bigIntType, decimalType).get()); + } + + @Test + public void testImplicitCastNumericWithExpectNumeric() { + BigIntType bigIntType = BigIntType.INSTANCE; + IntegerType integerType = IntegerType.INSTANCE; + Assertions.assertEquals(integerType, TypeCoercionUtils.implicitCast(bigIntType, integerType).get()); + } + + @Test + public void testImplicitCastStringToDecimal() { + StringType stringType = StringType.INSTANCE; + DecimalType decimalType = DecimalType.SYSTEM_DEFAULT; + Assertions.assertEquals(decimalType, TypeCoercionUtils.implicitCast(stringType, decimalType).get()); + } + + @Test + public void testImplicitCastStringToNumeric() { + VarcharType varcharType = VarcharType.createVarcharType(10); + IntegerType integerType = IntegerType.INSTANCE; + Assertions.assertEquals(integerType, TypeCoercionUtils.implicitCast(varcharType, integerType).get()); + } + + @Test + public void testImplicitCastFromPrimitiveToString() { + BigIntType bigIntType = BigIntType.INSTANCE; + StringType stringType = StringType.INSTANCE; + Assertions.assertEquals(stringType, TypeCoercionUtils.implicitCast(bigIntType, stringType).get()); + } + + @Test + public void testCannotImplicitCast() { + BigIntType bigIntType = BigIntType.INSTANCE; + NullType nullType = NullType.INSTANCE; + Assertions.assertFalse(TypeCoercionUtils.implicitCast(bigIntType, nullType).isPresent()); + } + + @Test + public void testCanHandleTypeCoercion() { + DecimalType decimalType = DecimalType.SYSTEM_DEFAULT; + NullType nullType = NullType.INSTANCE; + SmallIntType smallIntType = SmallIntType.INSTANCE; + IntegerType integerType = IntegerType.INSTANCE; + Assertions.assertTrue(TypeCoercionUtils.canHandleTypeCoercion(decimalType, nullType)); + Assertions.assertTrue(TypeCoercionUtils.canHandleTypeCoercion(nullType, decimalType)); + Assertions.assertTrue(TypeCoercionUtils.canHandleTypeCoercion(smallIntType, integerType)); + Assertions.assertFalse(TypeCoercionUtils.canHandleTypeCoercion(integerType, decimalType)); + Assertions.assertFalse(TypeCoercionUtils.canHandleTypeCoercion(decimalType, integerType)); + Assertions.assertFalse(TypeCoercionUtils.canHandleTypeCoercion(integerType, integerType)); + } + + @Test + public void testHasCharacterType() { + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(NullType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(BooleanType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(TinyIntType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(SmallIntType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(IntegerType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(BigIntType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(LargeIntType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(FloatType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(DoubleType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(DecimalType.SYSTEM_DEFAULT)); + Assertions.assertTrue(TypeCoercionUtils.hasCharacterType(CharType.createCharType(10))); + Assertions.assertTrue(TypeCoercionUtils.hasCharacterType(VarcharType.createVarcharType(10))); + Assertions.assertTrue(TypeCoercionUtils.hasCharacterType(StringType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(DateTimeType.INSTANCE)); + Assertions.assertFalse(TypeCoercionUtils.hasCharacterType(DateType.INSTANCE)); + } + + @Test + public void testFindTightestCommonType() { + testFindTightestCommonType(IntegerType.INSTANCE, IntegerType.INSTANCE, IntegerType.INSTANCE); + testFindTightestCommonType(IntegerType.INSTANCE, NullType.INSTANCE, IntegerType.INSTANCE); + testFindTightestCommonType(IntegerType.INSTANCE, IntegerType.INSTANCE, NullType.INSTANCE); + testFindTightestCommonType(DecimalType.SYSTEM_DEFAULT, IntegerType.INSTANCE, DecimalType.SYSTEM_DEFAULT); + testFindTightestCommonType(DecimalType.SYSTEM_DEFAULT, DecimalType.SYSTEM_DEFAULT, IntegerType.INSTANCE); + testFindTightestCommonType(BigIntType.INSTANCE, BigIntType.INSTANCE, IntegerType.INSTANCE); + testFindTightestCommonType(BigIntType.INSTANCE, IntegerType.INSTANCE, BigIntType.INSTANCE); + testFindTightestCommonType(StringType.INSTANCE, StringType.INSTANCE, IntegerType.INSTANCE); + testFindTightestCommonType(StringType.INSTANCE, IntegerType.INSTANCE, StringType.INSTANCE); + testFindTightestCommonType(null, DecimalType.SYSTEM_DEFAULT, DecimalType.createDecimalType(2, 1)); + } + + private void testFindTightestCommonType(DataType commonType, DataType left, DataType right) { + Assertions.assertEquals(Optional.ofNullable(commonType), TypeCoercionUtils.findTightestCommonType(left, right)); + } + + @Test + public void testFindWiderTypeForDecimal() { + Assertions.assertEquals(DecimalType.SYSTEM_DEFAULT, + TypeCoercionUtils.findWiderTypeForDecimal( + DecimalType.SYSTEM_DEFAULT, DecimalType.SYSTEM_DEFAULT).get()); + Assertions.assertEquals(DecimalType.SYSTEM_DEFAULT, + TypeCoercionUtils.findWiderTypeForDecimal( + DecimalType.SYSTEM_DEFAULT, TinyIntType.INSTANCE).get()); + Assertions.assertEquals(DecimalType.SYSTEM_DEFAULT, + TypeCoercionUtils.findWiderTypeForDecimal( + TinyIntType.INSTANCE, DecimalType.SYSTEM_DEFAULT).get()); + Assertions.assertEquals(DoubleType.INSTANCE, + TypeCoercionUtils.findWiderTypeForDecimal( + DecimalType.SYSTEM_DEFAULT, FloatType.INSTANCE).get()); + Assertions.assertEquals(DoubleType.INSTANCE, + TypeCoercionUtils.findWiderTypeForDecimal( + DoubleType.INSTANCE, DecimalType.SYSTEM_DEFAULT).get()); + Assertions.assertFalse(TypeCoercionUtils.findWiderTypeForDecimal( + StringType.INSTANCE, DecimalType.SYSTEM_DEFAULT).isPresent()); + Assertions.assertFalse(TypeCoercionUtils.findWiderTypeForDecimal( + DecimalType.SYSTEM_DEFAULT, StringType.INSTANCE).isPresent()); + } + + @Test + public void testCharacterPromotion() { + Assertions.assertEquals(StringType.INSTANCE, + TypeCoercionUtils.characterPromotion(StringType.INSTANCE, IntegerType.INSTANCE).get()); + Assertions.assertEquals(StringType.INSTANCE, + TypeCoercionUtils.characterPromotion(IntegerType.INSTANCE, StringType.INSTANCE).get()); + Assertions.assertFalse(TypeCoercionUtils.characterPromotion( + StringType.INSTANCE, BooleanType.INSTANCE).isPresent()); + Assertions.assertFalse(TypeCoercionUtils.characterPromotion( + BooleanType.INSTANCE, StringType.INSTANCE).isPresent()); + Assertions.assertFalse(TypeCoercionUtils.characterPromotion( + IntegerType.INSTANCE, IntegerType.INSTANCE).isPresent()); + } + + @Test + public void testCastIfNotSameType() { + Assertions.assertEquals(new DoubleLiteral(5L), + TypeCoercionUtils.castIfNotSameType(new DoubleLiteral(5L), DoubleType.INSTANCE)); + Assertions.assertEquals(new Cast(new DoubleLiteral(5L), BooleanType.INSTANCE), + TypeCoercionUtils.castIfNotSameType(new DoubleLiteral(5L), BooleanType.INSTANCE)); + } +} diff --git a/regression-test/data/nereids_syntax_p0/inpredicate.out b/regression-test/data/nereids_syntax_p0/inpredicate.out index d131768e01..b2bd323f6f 100644 --- a/regression-test/data/nereids_syntax_p0/inpredicate.out +++ b/regression-test/data/nereids_syntax_p0/inpredicate.out @@ -10,6 +10,13 @@ -- !in_predicate_4 -- +-- !in_predicate_5 -- + +-- !in_predicate_6 -- +15 Supplier#000000015 DF35PepL5saAK INDIA 0 INDIA ASIA 18-687-542-7601 +29 Supplier#000000029 VVSymB3fbwaN ARGENTINA4 ARGENTINA AMERICA 11-773-203-7342 +9 Supplier#000000009 ,gJ6K2MKveYxQT IRAN 6 IRAN MIDDLE EAST 20-338-906-3675 + -- !in_predicate_7 -- -- !in_predicate_8 -- diff --git a/regression-test/suites/nereids_syntax_p0/inpredicate.groovy b/regression-test/suites/nereids_syntax_p0/inpredicate.groovy index 60808108a2..d17a2dc198 100644 --- a/regression-test/suites/nereids_syntax_p0/inpredicate.groovy +++ b/regression-test/suites/nereids_syntax_p0/inpredicate.groovy @@ -40,14 +40,13 @@ suite("inpredicate") { SELECT * FROM supplier WHERE s_suppkey in (1, 2, 128, 32768, 32769); """ - // TODO: currently, we can not process difference type, so temporarily comment it -// order_qt_in_predicate_5 """ -// SELECT * FROM supplier WHERE s_suppkey in (1, 2, 128, 32768, 2147483648); -// """ -// -// order_qt_in_predicate_6 """ -// SELECT * FROM supplier WHERE s_suppkey not in (1, 2, 128, 32768, 2147483648); -// """ + order_qt_in_predicate_5 """ + SELECT * FROM supplier WHERE s_suppkey in (1, 2, 128, 32768, 2147483648); + """ + + order_qt_in_predicate_6 """ + SELECT * FROM supplier WHERE s_suppkey not in (1, 2, 128, 32768, 2147483648); + """ order_qt_in_predicate_7 """ SELECT * FROM supplier WHERE s_nation in ('PERU', 'ETHIOPIA');