This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch branch-3.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-3.1 by this push:
new 152528c8397 branch-3.1: [refactor](Nereids) refactor the parsing way
of date series functions #46037 (#52443)
152528c8397 is described below
commit 152528c839754581bd4dbae3de70661f22ddc30e
Author: morrySnow <[email protected]>
AuthorDate: Mon Jun 30 10:59:11 2025 +0800
branch-3.1: [refactor](Nereids) refactor the parsing way of date series
functions #46037 (#52443)
Cherry-picked #46037
---
.../antlr4/org/apache/doris/nereids/DorisLexer.g4 | 15 -
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 70 +--
.../doris/nereids/parser/LogicalPlanBuilder.java | 265 ---------
.../rules/analysis/ArithmeticFunctionBinder.java | 3 +
.../rules/analysis/DatetimeFunctionBinder.java | 348 ++++++++++++
.../nereids/rules/analysis/ExpressionAnalyzer.java | 54 +-
.../expressions/literal/DateTimeV2Literal.java | 3 +
.../trees/expressions/literal/Interval.java | 24 +-
.../doris/nereids/util/TypeCoercionUtils.java | 16 +
.../rules/analysis/DatetimeFunctionBinderTest.java | 626 +++++++++++++++++++++
10 files changed, 1060 insertions(+), 364 deletions(-)
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
index d55306f79da..730441b1253 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4
@@ -80,7 +80,6 @@ ACCOUNT_LOCK: 'ACCOUNT_LOCK';
ACCOUNT_UNLOCK: 'ACCOUNT_UNLOCK';
ACTIONS: 'ACTIONS';
ADD: 'ADD';
-ADDDATE:'ADDDATE';
ADMIN: 'ADMIN';
AFTER: 'AFTER';
AGG_STATE: 'AGG_STATE';
@@ -95,7 +94,6 @@ AND: 'AND';
ANTI: 'ANTI';
APPEND: 'APPEND';
ARRAY: 'ARRAY';
-ARRAY_RANGE: 'ARRAY_RANGE';
AS: 'AS';
ASC: 'ASC';
AT: 'AT';
@@ -182,21 +180,12 @@ DATA: 'DATA';
DATABASE: 'DATABASE';
DATABASES: 'DATABASES';
DATE: 'DATE';
-DATE_ADD: 'DATE_ADD';
-DATE_CEIL: 'DATE_CEIL';
-DATE_DIFF: 'DATE_DIFF';
-DATE_FLOOR: 'DATE_FLOOR';
-DATE_SUB: 'DATE_SUB';
-DATEADD: 'DATEADD';
-DATEDIFF: 'DATEDIFF';
DATETIME: 'DATETIME';
DATETIMEV2: 'DATETIMEV2';
DATEV2: 'DATEV2';
DATETIMEV1: 'DATETIMEV1';
DATEV1: 'DATEV1';
DAY: 'DAY';
-DAYS_ADD: 'DAYS_ADD';
-DAYS_SUB: 'DAYS_SUB';
DECIMAL: 'DECIMAL';
DECIMALV2: 'DECIMALV2';
DECIMALV3: 'DECIMALV3';
@@ -479,7 +468,6 @@ SCHEMAS: 'SCHEMAS';
SECOND: 'SECOND';
SELECT: 'SELECT';
SEMI: 'SEMI';
-SEQUENCE: 'SEQUENCE';
SERIALIZABLE: 'SERIALIZABLE';
SESSION: 'SESSION';
SESSION_USER: 'SESSION_USER';
@@ -508,7 +496,6 @@ STREAM: 'STREAM';
STREAMING: 'STREAMING';
STRING: 'STRING';
STRUCT: 'STRUCT';
-SUBDATE: 'SUBDATE';
SUM: 'SUM';
SUPERUSER: 'SUPERUSER';
SWITCH: 'SWITCH';
@@ -528,8 +515,6 @@ THAN: 'THAN';
THEN: 'THEN';
TIME: 'TIME';
TIMESTAMP: 'TIMESTAMP';
-TIMESTAMPADD: 'TIMESTAMPADD';
-TIMESTAMPDIFF: 'TIMESTAMPDIFF';
TINYINT: 'TINYINT';
TO: 'TO';
TOKENIZER: 'TOKENIZER';
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index 2e53b4187a4..efaebb8d445 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -1373,7 +1373,7 @@ fixedPartitionDef
;
stepPartitionDef
- : FROM from=partitionValueList TO to=partitionValueList INTERVAL
unitsAmount=INTEGER_VALUE unit=datetimeUnit?
+ : FROM from=partitionValueList TO to=partitionValueList INTERVAL
unitsAmount=INTEGER_VALUE unit=unitIdentifier?
;
inPartitionDef
@@ -1471,61 +1471,10 @@ valueExpression
| left=valueExpression operator=(PLUS | SUBTRACT | HAT | PIPE | AMPERSAND)
right=valueExpression
#arithmeticBinary
| left=valueExpression comparisonOperator right=valueExpression
#comparison
- | operator=(BITAND | BITOR | BITXOR) LEFT_PAREN left = valueExpression
- COMMA right = valueExpression RIGHT_PAREN
#bitOperation
- ;
-
-datetimeUnit
- : YEAR | MONTH
- | WEEK | DAY
- | HOUR | MINUTE | SECOND
;
primaryExpression
- : name=(TIMESTAMPDIFF | DATEDIFF)
- LEFT_PAREN
- unit=datetimeUnit COMMA
- startTimestamp=valueExpression COMMA
- endTimestamp=valueExpression
- RIGHT_PAREN
#timestampdiff
- | name=(TIMESTAMPADD | DATEADD)
- LEFT_PAREN
- unit=datetimeUnit COMMA
- startTimestamp=valueExpression COMMA
- endTimestamp=valueExpression
- RIGHT_PAREN
#timestampadd
- | name =(ADDDATE | DAYS_ADD | DATE_ADD)
- LEFT_PAREN
- timestamp=valueExpression COMMA
- (INTERVAL unitsAmount=valueExpression unit=datetimeUnit
- | unitsAmount=valueExpression)
- RIGHT_PAREN
#date_add
- | name=(SUBDATE | DAYS_SUB | DATE_SUB)
- LEFT_PAREN
- timestamp=valueExpression COMMA
- (INTERVAL unitsAmount=valueExpression unit=datetimeUnit
- | unitsAmount=valueExpression)
- RIGHT_PAREN
#date_sub
- | name=DATE_FLOOR
- LEFT_PAREN
- timestamp=valueExpression COMMA
- (INTERVAL unitsAmount=valueExpression unit=datetimeUnit
- | unitsAmount=valueExpression)
- RIGHT_PAREN
#dateFloor
- | name=DATE_CEIL
- LEFT_PAREN
- timestamp=valueExpression COMMA
- (INTERVAL unitsAmount=valueExpression unit=datetimeUnit
- | unitsAmount=valueExpression)
- RIGHT_PAREN
#dateCeil
- | name =(ARRAY_RANGE | SEQUENCE)
- LEFT_PAREN
- start=valueExpression COMMA
- end=valueExpression COMMA
- (INTERVAL unitsAmount=valueExpression unit=datetimeUnit
- | unitsAmount=valueExpression)
- RIGHT_PAREN
#arrayRange
- | name=CURRENT_DATE
#currentDate
+ : name=CURRENT_DATE
#currentDate
| name=CURRENT_TIME
#currentTime
| name=CURRENT_TIMESTAMP
#currentTimestamp
| name=LOCALTIME
#localTime
@@ -1794,7 +1743,6 @@ number
nonReserved
//--DEFAULT-NON-RESERVED-START
: ACTIONS
- | ADDDATE
| AFTER
| AGG_STATE
| AGGREGATE
@@ -1802,7 +1750,6 @@ nonReserved
| ALWAYS
| ANALYZED
| ARRAY
- | ARRAY_RANGE
| AT
| AUTHORS
| AUTO_INCREMENT
@@ -1867,21 +1814,12 @@ nonReserved
| CURRENT_USER
| DATA
| DATE
- | DATE_ADD
- | DATE_CEIL
- | DATE_DIFF
- | DATE_FLOOR
- | DATE_SUB
- | DATEADD
- | DATEDIFF
| DATETIME
| DATETIMEV1
| DATETIMEV2
| DATEV1
| DATEV2
| DAY
- | DAYS_ADD
- | DAYS_SUB
| DECIMAL
| DECIMALV2
| DECIMALV3
@@ -2062,7 +2000,6 @@ nonReserved
| SECOND
| SERIALIZABLE
| SET_SESSION_VARIABLE
- | SEQUENCE
| SESSION
| SESSION_USER
| SHAPE
@@ -2083,7 +2020,6 @@ nonReserved
| STREAMING
| STRING
| STRUCT
- | SUBDATE
| SUM
| TABLES
| TASK
@@ -2093,8 +2029,6 @@ nonReserved
| THAN
| TIME
| TIMESTAMP
- | TIMESTAMPADD
- | TIMESTAMPDIFF
| TRANSACTION
| TREE
| TRIGGERS
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 90a73c68b02..09c0855a828 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -56,9 +56,7 @@ import org.apache.doris.nereids.DorisParser.AlterViewContext;
import org.apache.doris.nereids.DorisParser.ArithmeticBinaryContext;
import org.apache.doris.nereids.DorisParser.ArithmeticUnaryContext;
import org.apache.doris.nereids.DorisParser.ArrayLiteralContext;
-import org.apache.doris.nereids.DorisParser.ArrayRangeContext;
import org.apache.doris.nereids.DorisParser.ArraySliceContext;
-import org.apache.doris.nereids.DorisParser.BitOperationContext;
import org.apache.doris.nereids.DorisParser.BooleanExpressionContext;
import org.apache.doris.nereids.DorisParser.BooleanLiteralContext;
import org.apache.doris.nereids.DorisParser.BracketDistributeTypeContext;
@@ -89,10 +87,6 @@ import
org.apache.doris.nereids.DorisParser.CreateTableLikeContext;
import org.apache.doris.nereids.DorisParser.CreateViewContext;
import org.apache.doris.nereids.DorisParser.CteContext;
import org.apache.doris.nereids.DorisParser.DataTypeWithNullableContext;
-import org.apache.doris.nereids.DorisParser.DateCeilContext;
-import org.apache.doris.nereids.DorisParser.DateFloorContext;
-import org.apache.doris.nereids.DorisParser.Date_addContext;
-import org.apache.doris.nereids.DorisParser.Date_subContext;
import org.apache.doris.nereids.DorisParser.DecimalLiteralContext;
import org.apache.doris.nereids.DorisParser.DeleteContext;
import org.apache.doris.nereids.DorisParser.DereferenceContext;
@@ -206,8 +200,6 @@ import
org.apache.doris.nereids.DorisParser.SystemVariableContext;
import org.apache.doris.nereids.DorisParser.TableAliasContext;
import org.apache.doris.nereids.DorisParser.TableNameContext;
import org.apache.doris.nereids.DorisParser.TableValuedFunctionContext;
-import org.apache.doris.nereids.DorisParser.TimestampaddContext;
-import org.apache.doris.nereids.DorisParser.TimestampdiffContext;
import org.apache.doris.nereids.DorisParser.TypeConstructorContext;
import org.apache.doris.nereids.DorisParser.UnitIdentifierContext;
import org.apache.doris.nereids.DorisParser.UnsupportedContext;
@@ -298,61 +290,18 @@ import
org.apache.doris.nereids.trees.expressions.WindowFrame;
import org.apache.doris.nereids.trees.expressions.functions.Function;
import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Array;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRange;
-import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeDayUnit;
-import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeHourUnit;
-import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeMinuteUnit;
-import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeMonthUnit;
-import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeSecondUnit;
-import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeWeekUnit;
-import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeYearUnit;
import org.apache.doris.nereids.trees.expressions.functions.scalar.ArraySlice;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Char;
import org.apache.doris.nereids.trees.expressions.functions.scalar.ConvertTo;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentDate;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentTime;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentUser;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.DayCeil;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.DayFloor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
import org.apache.doris.nereids.trees.expressions.functions.scalar.ElementAt;
import
org.apache.doris.nereids.trees.expressions.functions.scalar.EncryptKeyRef;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.HourCeil;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.HourFloor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursAdd;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursDiff;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursSub;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Lambda;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteCeil;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteFloor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesAdd;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesDiff;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesSub;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthCeil;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthFloor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsAdd;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsDiff;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsSub;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Now;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondCeil;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondFloor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsAdd;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsDiff;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsSub;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SessionUser;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekCeil;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekFloor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksAdd;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksDiff;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksSub;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Xor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.YearCeil;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.YearFloor;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff;
-import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsSub;
import org.apache.doris.nereids.trees.expressions.literal.ArrayLiteral;
import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
@@ -1863,22 +1812,6 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
});
}
- @Override
- public Expression visitBitOperation(BitOperationContext ctx) {
- return ParserUtils.withOrigin(ctx, () -> {
- Expression left = getExpression(ctx.left);
- Expression right = getExpression(ctx.right);
- if (ctx.operator.getType() == DorisParser.BITAND) {
- return new BitAnd(left, right);
- } else if (ctx.operator.getType() == DorisParser.BITOR) {
- return new BitOr(left, right);
- } else if (ctx.operator.getType() == DorisParser.BITXOR) {
- return new BitXor(left, right);
- }
- throw new ParseException(" not supported", ctx);
- });
- }
-
@Override
public Expression visitArithmeticBinary(ArithmeticBinaryContext ctx) {
return ParserUtils.withOrigin(ctx, () -> {
@@ -1935,204 +1868,6 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
});
}
- @Override
- public Expression visitTimestampdiff(TimestampdiffContext ctx) {
- Expression start = (Expression) visit(ctx.startTimestamp);
- Expression end = (Expression) visit(ctx.endTimestamp);
- String unit = ctx.unit.getText();
- if ("YEAR".equalsIgnoreCase(unit)) {
- return new YearsDiff(end, start);
- } else if ("MONTH".equalsIgnoreCase(unit)) {
- return new MonthsDiff(end, start);
- } else if ("WEEK".equalsIgnoreCase(unit)) {
- return new WeeksDiff(end, start);
- } else if ("DAY".equalsIgnoreCase(unit)) {
- return new DaysDiff(end, start);
- } else if ("HOUR".equalsIgnoreCase(unit)) {
- return new HoursDiff(end, start);
- } else if ("MINUTE".equalsIgnoreCase(unit)) {
- return new MinutesDiff(end, start);
- } else if ("SECOND".equalsIgnoreCase(unit)) {
- return new SecondsDiff(end, start);
- }
- throw new ParseException("Unsupported time stamp diff time unit: " +
unit
- + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND", ctx);
-
- }
-
- @Override
- public Expression visitTimestampadd(TimestampaddContext ctx) {
- Expression start = (Expression) visit(ctx.startTimestamp);
- Expression end = (Expression) visit(ctx.endTimestamp);
- String unit = ctx.unit.getText();
- if ("YEAR".equalsIgnoreCase(unit)) {
- return new YearsAdd(end, start);
- } else if ("MONTH".equalsIgnoreCase(unit)) {
- return new MonthsAdd(end, start);
- } else if ("WEEK".equalsIgnoreCase(unit)) {
- return new WeeksAdd(end, start);
- } else if ("DAY".equalsIgnoreCase(unit)) {
- return new DaysAdd(end, start);
- } else if ("HOUR".equalsIgnoreCase(unit)) {
- return new HoursAdd(end, start);
- } else if ("MINUTE".equalsIgnoreCase(unit)) {
- return new MinutesAdd(end, start);
- } else if ("SECOND".equalsIgnoreCase(unit)) {
- return new SecondsAdd(end, start);
- }
- throw new ParseException("Unsupported time stamp add time unit: " +
unit
- + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND", ctx);
-
- }
-
- @Override
- public Expression visitDate_add(Date_addContext ctx) {
- Expression timeStamp = (Expression) visit(ctx.timestamp);
- Expression amount = (Expression) visit(ctx.unitsAmount);
- if (ctx.unit == null) {
- //use "DAY" as unit by default
- return new DaysAdd(timeStamp, amount);
- }
-
- if ("Year".equalsIgnoreCase(ctx.unit.getText())) {
- return new YearsAdd(timeStamp, amount);
- } else if ("MONTH".equalsIgnoreCase(ctx.unit.getText())) {
- return new MonthsAdd(timeStamp, amount);
- } else if ("WEEK".equalsIgnoreCase(ctx.unit.getText())) {
- return new WeeksAdd(timeStamp, amount);
- } else if ("DAY".equalsIgnoreCase(ctx.unit.getText())) {
- return new DaysAdd(timeStamp, amount);
- } else if ("Hour".equalsIgnoreCase(ctx.unit.getText())) {
- return new HoursAdd(timeStamp, amount);
- } else if ("Minute".equalsIgnoreCase(ctx.unit.getText())) {
- return new MinutesAdd(timeStamp, amount);
- } else if ("Second".equalsIgnoreCase(ctx.unit.getText())) {
- return new SecondsAdd(timeStamp, amount);
- }
- throw new ParseException("Unsupported time unit: " + ctx.unit
- + ", supported time unit: YEAR/MONTH/DAY/HOUR/MINUTE/SECOND",
ctx);
- }
-
- @Override
- public Expression visitArrayRange(ArrayRangeContext ctx) {
- Expression start = (Expression) visit(ctx.start);
- Expression end = (Expression) visit(ctx.end);
- Expression step = (Expression) visit(ctx.unitsAmount);
-
- String unit = ctx.unit == null ? null : ctx.unit.getText();
- if (unit != null && !unit.isEmpty()) {
- if ("Year".equalsIgnoreCase(unit)) {
- return new ArrayRangeYearUnit(start, end, step);
- } else if ("Month".equalsIgnoreCase(unit)) {
- return new ArrayRangeMonthUnit(start, end, step);
- } else if ("Week".equalsIgnoreCase(unit)) {
- return new ArrayRangeWeekUnit(start, end, step);
- } else if ("Day".equalsIgnoreCase(unit)) {
- return new ArrayRangeDayUnit(start, end, step);
- } else if ("Hour".equalsIgnoreCase(unit)) {
- return new ArrayRangeHourUnit(start, end, step);
- } else if ("Minute".equalsIgnoreCase(unit)) {
- return new ArrayRangeMinuteUnit(start, end, step);
- } else if ("Second".equalsIgnoreCase(unit)) {
- return new ArrayRangeSecondUnit(start, end, step);
- }
- throw new ParseException("Unsupported time unit: " + ctx.unit
- + ", supported time unit:
YEAR/MONTH/DAY/HOUR/MINUTE/SECOND", ctx);
- } else if (ctx.unitsAmount != null) {
- return new ArrayRange(start, end, step);
- } else if (ctx.end != null) {
- return new ArrayRange(start, end);
- } else {
- return new ArrayRange(start);
- }
- }
-
- @Override
- public Expression visitDate_sub(Date_subContext ctx) {
- Expression timeStamp = (Expression) visit(ctx.timestamp);
- Expression amount = (Expression) visit(ctx.unitsAmount);
- if (ctx.unit == null) {
- //use "DAY" as unit by default
- return new DaysSub(timeStamp, amount);
- }
-
- if ("Year".equalsIgnoreCase(ctx.unit.getText())) {
- return new YearsSub(timeStamp, amount);
- } else if ("MONTH".equalsIgnoreCase(ctx.unit.getText())) {
- return new MonthsSub(timeStamp, amount);
- } else if ("WEEK".equalsIgnoreCase(ctx.unit.getText())) {
- return new WeeksSub(timeStamp, amount);
- } else if ("DAY".equalsIgnoreCase(ctx.unit.getText())) {
- return new DaysSub(timeStamp, amount);
- } else if ("Hour".equalsIgnoreCase(ctx.unit.getText())) {
- return new HoursSub(timeStamp, amount);
- } else if ("Minute".equalsIgnoreCase(ctx.unit.getText())) {
- return new MinutesSub(timeStamp, amount);
- } else if ("Second".equalsIgnoreCase(ctx.unit.getText())) {
- return new SecondsSub(timeStamp, amount);
- }
- throw new ParseException("Unsupported time unit: " + ctx.unit
- + ", supported time unit: YEAR/MONTH/DAY/HOUR/MINUTE/SECOND",
ctx);
- }
-
- @Override
- public Expression visitDateFloor(DateFloorContext ctx) {
- Expression timeStamp = (Expression) visit(ctx.timestamp);
- Expression amount = (Expression) visit(ctx.unitsAmount);
- if (ctx.unit == null) {
- // use "SECOND" as unit by default
- return new SecondFloor(timeStamp, amount);
- }
- Expression e = new DateTimeV2Literal(0001L, 01L, 01L, 0L, 0L, 0L, 0L);
-
- if ("Year".equalsIgnoreCase(ctx.unit.getText())) {
- return new YearFloor(timeStamp, amount, e);
- } else if ("MONTH".equalsIgnoreCase(ctx.unit.getText())) {
- return new MonthFloor(timeStamp, amount, e);
- } else if ("WEEK".equalsIgnoreCase(ctx.unit.getText())) {
- return new WeekFloor(timeStamp, amount, e);
- } else if ("DAY".equalsIgnoreCase(ctx.unit.getText())) {
- return new DayFloor(timeStamp, amount, e);
- } else if ("Hour".equalsIgnoreCase(ctx.unit.getText())) {
- return new HourFloor(timeStamp, amount, e);
- } else if ("Minute".equalsIgnoreCase(ctx.unit.getText())) {
- return new MinuteFloor(timeStamp, amount, e);
- } else if ("Second".equalsIgnoreCase(ctx.unit.getText())) {
- return new SecondFloor(timeStamp, amount, e);
- }
- throw new ParseException("Unsupported time unit: " + ctx.unit
- + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND", ctx);
- }
-
- @Override
- public Expression visitDateCeil(DateCeilContext ctx) {
- Expression timeStamp = (Expression) visit(ctx.timestamp);
- Expression amount = (Expression) visit(ctx.unitsAmount);
- if (ctx.unit == null) {
- // use "Second" as unit by default
- return new SecondCeil(timeStamp, amount);
- }
- DateTimeV2Literal e = new DateTimeV2Literal(0001L, 01L, 01L, 0L, 0L,
0L, 0L);
-
- if ("Year".equalsIgnoreCase(ctx.unit.getText())) {
- return new YearCeil(timeStamp, amount, e);
- } else if ("MONTH".equalsIgnoreCase(ctx.unit.getText())) {
- return new MonthCeil(timeStamp, amount, e);
- } else if ("WEEK".equalsIgnoreCase(ctx.unit.getText())) {
- return new WeekCeil(timeStamp, amount, e);
- } else if ("DAY".equalsIgnoreCase(ctx.unit.getText())) {
- return new DayCeil(timeStamp, amount, e);
- } else if ("Hour".equalsIgnoreCase(ctx.unit.getText())) {
- return new HourCeil(timeStamp, amount, e);
- } else if ("Minute".equalsIgnoreCase(ctx.unit.getText())) {
- return new MinuteCeil(timeStamp, amount, e);
- } else if ("Second".equalsIgnoreCase(ctx.unit.getText())) {
- return new SecondCeil(timeStamp, amount, e);
- }
- throw new ParseException("Unsupported time unit: " + ctx.unit
- + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND", ctx);
- }
-
@Override
public Expression visitCurrentDate(DorisParser.CurrentDateContext ctx) {
return new CurrentDate();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ArithmeticFunctionBinder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ArithmeticFunctionBinder.java
index 0a1d2a788dd..102637a2abc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ArithmeticFunctionBinder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ArithmeticFunctionBinder.java
@@ -39,6 +39,9 @@ import java.util.Map;
* bind arithmetic function
*/
public class ArithmeticFunctionBinder {
+
+ public static final ArithmeticFunctionBinder INSTANCE = new
ArithmeticFunctionBinder();
+
private static final NullLiteral DUMMY_EXPRESSION = new NullLiteral();
private static final Map<String, Expression> FUNCTION_TO_EXPRESSION =
ImmutableMap.<String, Expression>builder()
.put("add", new Add(DUMMY_EXPRESSION, DUMMY_EXPRESSION))
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java
new file mode 100644
index 00000000000..c45301893d5
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java
@@ -0,0 +1,348 @@
+// 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.analyzer.UnboundFunction;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRange;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeDayUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeHourUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeMinuteUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeMonthUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeSecondUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeWeekUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeYearUnit;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DateDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HourCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HourFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsSub;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.Interval;
+import org.apache.doris.nereids.trees.expressions.literal.Interval.TimeUnit;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * bind arithmetic function
+ */
+public class DatetimeFunctionBinder {
+
+ public static final DatetimeFunctionBinder INSTANCE = new
DatetimeFunctionBinder();
+
+ private static final String DATEDIFF = "DATEDIFF";
+
+ private static final ImmutableSet<String> TIMESTAMP_DIFF_FUNCTION_NAMES
+ = ImmutableSet.of("TIMESTAMPDIFF", DATEDIFF);
+ private static final ImmutableSet<String> TIMESTAMP_ADD_FUNCTION_NAMES
+ = ImmutableSet.of("TIMESTAMPADD", "DATEADD");
+ public static final ImmutableSet<String> TIMESTAMP_SERIES_FUNCTION_NAMES
+ = ImmutableSet.<String>builder()
+ .addAll(TIMESTAMP_DIFF_FUNCTION_NAMES)
+ .addAll(TIMESTAMP_ADD_FUNCTION_NAMES)
+ .build();
+
+ private static final ImmutableSet<String> ADD_DATE_FUNCTION_NAMES
+ = ImmutableSet.of("ADDDATE", "DAYS_ADD", "DATE_ADD");
+ private static final ImmutableSet<String> SUB_DATE_FUNCTION_NAMES
+ = ImmutableSet.of("SUBDATE", "DAYS_SUB", "DATE_SUB");
+ private static final ImmutableSet<String>
DATE_ADD_SUB_SERIES_FUNCTION_NAMES
+ = ImmutableSet.<String>builder()
+ .addAll(ADD_DATE_FUNCTION_NAMES)
+ .addAll(SUB_DATE_FUNCTION_NAMES)
+ .build();
+ private static final ImmutableSet<String> DATE_FLOOR_FUNCTION_NAMES
+ = ImmutableSet.of("DATE_FLOOR");
+ private static final ImmutableSet<String> DATE_CEIL_FUNCTION_NAMES
+ = ImmutableSet.of("DATE_CEIL");
+ private static final ImmutableSet<String>
DATE_FLOOR_CEIL_SERIES_FUNCTION_NAMES
+ = ImmutableSet.<String>builder()
+ .addAll(DATE_FLOOR_FUNCTION_NAMES)
+ .addAll(DATE_CEIL_FUNCTION_NAMES)
+ .build();
+ private static final ImmutableSet<String> DATE_SERIES_FUNCTION_NAMES
+ = ImmutableSet.<String>builder()
+ .addAll(DATE_ADD_SUB_SERIES_FUNCTION_NAMES)
+ .addAll(DATE_FLOOR_CEIL_SERIES_FUNCTION_NAMES)
+ .build();
+
+ private static final ImmutableSet<String> ARRAY_RANGE_FUNCTION_NAMES
+ = ImmutableSet.of("ARRAY_RANGE", "SEQUENCE");
+
+ private static final ImmutableSet<String> SUPPORT_FUNCTION_NAMES
+ = ImmutableSet.<String>builder()
+ .addAll(TIMESTAMP_SERIES_FUNCTION_NAMES)
+ .addAll(DATE_SERIES_FUNCTION_NAMES)
+ .addAll(ARRAY_RANGE_FUNCTION_NAMES)
+ .build();
+
+ public boolean isDatetimeFunction(String functionName) {
+ return SUPPORT_FUNCTION_NAMES.contains(functionName.toUpperCase());
+ }
+
+ /**
+ * bind datetime functions that have non-expression arguments.
+ *
+ * @param unboundFunction unbound datetime function
+ *
+ * @return bound function
+ */
+ public Expression bind(UnboundFunction unboundFunction) {
+ String functionName = unboundFunction.getName().toUpperCase();
+ if (TIMESTAMP_SERIES_FUNCTION_NAMES.contains(functionName)) {
+ if (unboundFunction.arity() == 2 && functionName.equals(DATEDIFF))
{
+ return new DateDiff(unboundFunction.child(0),
unboundFunction.child(1));
+ } else if (unboundFunction.arity() != 3
+ || !(unboundFunction.child(0) instanceof SlotReference)) {
+ throw new AnalysisException("Can not found function '" +
functionName
+ + "' with " + unboundFunction.arity() + " arguments");
+ }
+ String unitName = ((SlotReference)
unboundFunction.child(0)).getName().toUpperCase();
+ TimeUnit unit;
+ try {
+ unit = TimeUnit.valueOf(unitName);
+ } catch (IllegalArgumentException e) {
+ throw new AnalysisException("Unsupported time stamp diff time
unit: " + unitName
+ + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
+ }
+ if (TIMESTAMP_DIFF_FUNCTION_NAMES.contains(functionName)) {
+ // timestampdiff(unit, start, end)
+ return processTimestampDiff(unit, unboundFunction.child(1),
unboundFunction.child(2));
+ } else {
+ // timestampadd(unit, amount, basetime)
+ return processDateAdd(unit, unboundFunction.child(2),
unboundFunction.child(1));
+ }
+ } else if (DATE_SERIES_FUNCTION_NAMES.contains(functionName)) {
+ if (unboundFunction.arity() != 2) {
+ throw new AnalysisException("Can not found function '" +
functionName
+ + "' with " + unboundFunction.arity() + " arguments");
+ }
+ // date_add and date_sub's default unit is DAY, date_ceil and
date_floor's default unit is SECOND
+ TimeUnit unit = TimeUnit.DAY;
+ if (DATE_FLOOR_CEIL_SERIES_FUNCTION_NAMES.contains(functionName)) {
+ unit = TimeUnit.SECOND;
+ }
+ Expression amount = unboundFunction.child(1);
+ if (unboundFunction.child(1) instanceof Interval) {
+ Interval interval = (Interval) unboundFunction.child(1);
+ unit = interval.timeUnit();
+ amount = interval.value();
+ }
+ if (ADD_DATE_FUNCTION_NAMES.contains(functionName)) {
+ // date_add(date, interval amount unit | amount)
+ return processDateAdd(unit, unboundFunction.child(0), amount);
+ } else if (SUB_DATE_FUNCTION_NAMES.contains(functionName)) {
+ // date_add(date, interval amount unit | amount)
+ return processDateSub(unit, unboundFunction.child(0), amount);
+ } else if (DATE_FLOOR_FUNCTION_NAMES.contains(functionName)) {
+ // date_floor(date, interval amount unit | amount)
+ return processDateFloor(unit, unboundFunction.child(0),
amount);
+ } else {
+ // date_ceil(date, interval amount unit | amount)
+ return processDateCeil(unit, unboundFunction.child(0), amount);
+ }
+ } else if (ARRAY_RANGE_FUNCTION_NAMES.contains(functionName)) {
+ switch (unboundFunction.arity()) {
+ case 1:
+ return new ArrayRange(unboundFunction.child(0));
+ case 2:
+ return new ArrayRange(unboundFunction.child(0),
unboundFunction.child(1));
+ case 3:
+ if (unboundFunction.child(2) instanceof Interval) {
+ Interval interval = (Interval)
unboundFunction.child(2);
+ TimeUnit unit = interval.timeUnit();
+ Expression step = interval.value();
+ return processArrayRange(unit,
unboundFunction.child(0), unboundFunction.child(1), step);
+ }
+ return new ArrayRange(unboundFunction.child(0),
+ unboundFunction.child(1),
unboundFunction.child(2));
+ default:
+ throw new AnalysisException("Can not found function '" +
functionName + "'");
+ }
+ }
+ throw new AnalysisException("Can not found function '" + functionName
+ "'");
+ }
+
+ private Expression processTimestampDiff(TimeUnit unit, Expression start,
Expression end) {
+ switch (unit) {
+ case YEAR:
+ return new YearsDiff(end, start);
+ case MONTH:
+ return new MonthsDiff(end, start);
+ case WEEK:
+ return new WeeksDiff(end, start);
+ case DAY:
+ return new DaysDiff(end, start);
+ case HOUR:
+ return new HoursDiff(end, start);
+ case MINUTE:
+ return new MinutesDiff(end, start);
+ case SECOND:
+ return new SecondsDiff(end, start);
+ default:
+ throw new AnalysisException("Unsupported time stamp diff time
unit: " + unit
+ + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
+ }
+ }
+
+ private Expression processDateAdd(TimeUnit unit, Expression timestamp,
Expression amount) {
+ switch (unit) {
+ case YEAR:
+ return new YearsAdd(timestamp, amount);
+ case MONTH:
+ return new MonthsAdd(timestamp, amount);
+ case WEEK:
+ return new WeeksAdd(timestamp, amount);
+ case DAY:
+ return new DaysAdd(timestamp, amount);
+ case HOUR:
+ return new HoursAdd(timestamp, amount);
+ case MINUTE:
+ return new MinutesAdd(timestamp, amount);
+ case SECOND:
+ return new SecondsAdd(timestamp, amount);
+ default:
+ throw new AnalysisException("Unsupported time stamp add time
unit: " + unit
+ + ", supported time unit:
YEAR/QUARTER/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
+ }
+ }
+
+ private Expression processDateSub(TimeUnit unit, Expression timeStamp,
Expression amount) {
+ switch (unit) {
+ case YEAR:
+ return new YearsSub(timeStamp, amount);
+ case MONTH:
+ return new MonthsSub(timeStamp, amount);
+ case WEEK:
+ return new WeeksSub(timeStamp, amount);
+ case DAY:
+ return new DaysSub(timeStamp, amount);
+ case HOUR:
+ return new HoursSub(timeStamp, amount);
+ case MINUTE:
+ return new MinutesSub(timeStamp, amount);
+ case SECOND:
+ return new SecondsSub(timeStamp, amount);
+ default:
+ throw new AnalysisException("Unsupported time stamp sub time
unit: " + unit
+ + ", supported time unit:
YEAR/QUARTER/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
+ }
+ }
+
+ private Expression processDateFloor(TimeUnit unit, Expression timeStamp,
Expression amount) {
+ DateTimeV2Literal e = DateTimeV2Literal.USE_IN_FLOOR_CEIL;
+ switch (unit) {
+ case YEAR:
+ return new YearFloor(timeStamp, amount, e);
+ case MONTH:
+ return new MonthFloor(timeStamp, amount, e);
+ case WEEK:
+ return new WeekFloor(timeStamp, amount, e);
+ case DAY:
+ return new DayFloor(timeStamp, amount, e);
+ case HOUR:
+ return new HourFloor(timeStamp, amount, e);
+ case MINUTE:
+ return new MinuteFloor(timeStamp, amount, e);
+ case SECOND:
+ return new SecondFloor(timeStamp, amount, e);
+ default:
+ throw new AnalysisException("Unsupported time stamp floor time
unit: " + unit
+ + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
+ }
+ }
+
+ private Expression processDateCeil(TimeUnit unit, Expression timeStamp,
Expression amount) {
+ DateTimeV2Literal e = DateTimeV2Literal.USE_IN_FLOOR_CEIL;
+ switch (unit) {
+ case YEAR:
+ return new YearCeil(timeStamp, amount, e);
+ case MONTH:
+ return new MonthCeil(timeStamp, amount, e);
+ case WEEK:
+ return new WeekCeil(timeStamp, amount, e);
+ case DAY:
+ return new DayCeil(timeStamp, amount, e);
+ case HOUR:
+ return new HourCeil(timeStamp, amount, e);
+ case MINUTE:
+ return new MinuteCeil(timeStamp, amount, e);
+ case SECOND:
+ return new SecondCeil(timeStamp, amount, e);
+ default:
+ throw new AnalysisException("Unsupported time stamp ceil time
unit: " + unit
+ + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
+ }
+ }
+
+ private Expression processArrayRange(TimeUnit unit, Expression start,
Expression end, Expression step) {
+ switch (unit) {
+ case YEAR:
+ return new ArrayRangeYearUnit(start, end, step);
+ case MONTH:
+ return new ArrayRangeMonthUnit(start, end, step);
+ case WEEK:
+ return new ArrayRangeWeekUnit(start, end, step);
+ case DAY:
+ return new ArrayRangeDayUnit(start, end, step);
+ case HOUR:
+ return new ArrayRangeHourUnit(start, end, step);
+ case MINUTE:
+ return new ArrayRangeMinuteUnit(start, end, step);
+ case SECOND:
+ return new ArrayRangeSecondUnit(start, end, step);
+ default:
+ throw new AnalysisException("Unsupported array range time
unit: " + unit
+ + ", supported time unit:
YEAR/MONTH/WEEK/DAY/HOUR/MINUTE/SECOND");
+ }
+ }
+
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
index 5b6295d1bc2..a0d5579abdc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java
@@ -50,6 +50,7 @@ import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
import org.apache.doris.nereids.trees.expressions.Divide;
import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.ExprId;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.InPredicate;
import org.apache.doris.nereids.trees.expressions.InSubquery;
@@ -86,6 +87,7 @@ import org.apache.doris.nereids.types.ArrayType;
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.TinyIntType;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.TypeCoercionUtils;
import org.apache.doris.nereids.util.Utils;
@@ -375,6 +377,26 @@ public class ExpressionAnalyzer extends
SubExprAnalyzer<ExpressionRewriteContext
if (unboundFunction.isHighOrder()) {
unboundFunction = bindHighOrderFunction(unboundFunction, context);
} else {
+ // NOTICE: some trick code here. because below functions
+ // TIMESTAMPADD / DATEDIFF / TIMESTAMPADD / DATEADD
+ // the first argument of them is TimeUnit, but is cannot
distinguish with UnboundSlot in parser.
+ // So, convert the UnboundSlot to a fake SlotReference with
ExprId = -1 here
+ // And, the SlotReference will be processed in
DatetimeFunctionBinder
+ if (StringUtils.isEmpty(unboundFunction.getDbName())
+ &&
DatetimeFunctionBinder.TIMESTAMP_SERIES_FUNCTION_NAMES.contains(
+ unboundFunction.getName().toUpperCase())
+ && unboundFunction.arity() == 3
+ && unboundFunction.child(0) instanceof UnboundSlot) {
+ SlotReference slotReference = new SlotReference(new ExprId(-1),
+ ((UnboundSlot) unboundFunction.child(0)).getName(),
+ TinyIntType.INSTANCE, true, ImmutableList.of());
+ ImmutableList.Builder<Expression> newChildrenBuilder =
ImmutableList.builder();
+ newChildrenBuilder.add(slotReference);
+ for (int i = 1; i < unboundFunction.arity(); i++) {
+ newChildrenBuilder.add(unboundFunction.child(i));
+ }
+ unboundFunction =
unboundFunction.withChildren(newChildrenBuilder.build());
+ }
unboundFunction = (UnboundFunction) super.visit(unboundFunction,
context);
}
@@ -391,10 +413,28 @@ public class ExpressionAnalyzer extends
SubExprAnalyzer<ExpressionRewriteContext
if (StringUtils.isEmpty(dbName)) {
// we will change arithmetic function like add(), subtract(),
bitnot()
// to the corresponding objects rather than BoundFunction.
- ArithmeticFunctionBinder functionBinder = new
ArithmeticFunctionBinder();
- if (functionBinder.isBinaryArithmetic(unboundFunction.getName())) {
- return
functionBinder.bindBinaryArithmetic(unboundFunction.getName(),
unboundFunction.children())
- .accept(this, context);
+ if
(ArithmeticFunctionBinder.INSTANCE.isBinaryArithmetic(unboundFunction.getName()))
{
+ Expression ret =
ArithmeticFunctionBinder.INSTANCE.bindBinaryArithmetic(
+ unboundFunction.getName(), unboundFunction.children());
+ if (ret instanceof Divide) {
+ return TypeCoercionUtils.processDivide((Divide) ret);
+ } else if (ret instanceof IntegralDivide) {
+ return
TypeCoercionUtils.processIntegralDivide((IntegralDivide) ret);
+ } else if ((ret instanceof BinaryArithmetic)) {
+ return
TypeCoercionUtils.processBinaryArithmetic((BinaryArithmetic) ret);
+ } else if (ret instanceof BitNot) {
+ return TypeCoercionUtils.processBitNot((BitNot) ret);
+ } else {
+ return ret;
+ }
+ }
+ if
(DatetimeFunctionBinder.INSTANCE.isDatetimeFunction(unboundFunction.getName()))
{
+ Expression ret =
DatetimeFunctionBinder.INSTANCE.bind(unboundFunction);
+ if (ret instanceof BoundFunction) {
+ return
TypeCoercionUtils.processBoundFunction((BoundFunction) ret);
+ } else {
+ return ret;
+ }
}
}
@@ -481,11 +521,7 @@ public class ExpressionAnalyzer extends
SubExprAnalyzer<ExpressionRewriteContext
@Override
public Expression visitBitNot(BitNot bitNot, ExpressionRewriteContext
context) {
Expression child = bitNot.child().accept(this, context);
- // type coercion
- if (!(child.getDataType().isIntegralType() ||
child.getDataType().isBooleanType())) {
- child = new Cast(child, BigIntType.INSTANCE);
- }
- return bitNot.withChildren(child);
+ return TypeCoercionUtils.processBitNot((BitNot)
bitNot.withChildren(child));
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
index 69a75e001ab..599e903df39 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
@@ -36,6 +36,9 @@ import java.util.Objects;
*/
public class DateTimeV2Literal extends DateTimeLiteral {
+ public static final DateTimeV2Literal USE_IN_FLOOR_CEIL
+ = new DateTimeV2Literal(0001L, 01L, 01L, 0L, 0L, 0L, 0L);
+
public DateTimeV2Literal(String s) {
this(DateTimeV2Type.forTypeFromString(s), s);
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
index 454001fb3f1..246122e59db 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
@@ -19,27 +19,31 @@ package org.apache.doris.nereids.trees.expressions.literal;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
-import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
+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 org.apache.doris.nereids.types.DateType;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.apache.commons.lang3.EnumUtils;
+import java.util.List;
import java.util.Optional;
/**
* Interval for timestamp calculation.
*/
-public class Interval extends Expression implements LeafExpression,
AlwaysNotNullable {
- private final Expression value;
+public class Interval extends Expression implements UnaryExpression,
AlwaysNotNullable {
private final TimeUnit timeUnit;
public Interval(Expression value, String desc) {
- super(ImmutableList.of());
- this.value = value;
- this.timeUnit = TimeUnit.valueOf(desc.toUpperCase());
+ this(value, TimeUnit.valueOf(desc.toUpperCase()));
+ }
+
+ public Interval(Expression value, TimeUnit timeUnit) {
+ super(ImmutableList.of(value));
+ this.timeUnit = timeUnit;
}
@Override
@@ -48,13 +52,19 @@ public class Interval extends Expression implements
LeafExpression, AlwaysNotNul
}
public Expression value() {
- return value;
+ return child();
}
public TimeUnit timeUnit() {
return timeUnit;
}
+ @Override
+ public Expression withChildren(List<Expression> children) {
+ Preconditions.checkArgument(children.size() == 1);
+ return new Interval(children.get(0), timeUnit);
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitInterval(this, context);
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
index 64457acee15..15de1fd4c22 100644
---
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
@@ -29,6 +29,7 @@ import org.apache.doris.nereids.trees.expressions.Add;
import org.apache.doris.nereids.trees.expressions.BinaryArithmetic;
import org.apache.doris.nereids.trees.expressions.BinaryOperator;
import org.apache.doris.nereids.trees.expressions.BitAnd;
+import org.apache.doris.nereids.trees.expressions.BitNot;
import org.apache.doris.nereids.trees.expressions.BitOr;
import org.apache.doris.nereids.trees.expressions.BitXor;
import org.apache.doris.nereids.trees.expressions.CaseWhen;
@@ -805,6 +806,21 @@ public class TypeCoercionUtils {
return parent.withChildren(castIfNotSameType(left, commonType),
castIfNotSameType(right, commonType));
}
+ /**
+ * process BitNot type coercion, cast child to bigint.
+ */
+ public static Expression processBitNot(BitNot bitNot) {
+ Expression child = bitNot.child();
+ if (!(child.getDataType().isIntegralType() ||
child.getDataType().isBooleanType())) {
+ child = new Cast(child, BigIntType.INSTANCE);
+ }
+ if (child != bitNot.child()) {
+ return bitNot.withChildren(child);
+ } else {
+ return bitNot;
+ }
+ }
+
/**
* binary arithmetic type coercion
*/
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinderTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinderTest.java
new file mode 100644
index 00000000000..94f772f9508
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinderTest.java
@@ -0,0 +1,626 @@
+// 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.analyzer.UnboundFunction;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRange;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeDayUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeHourUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeMinuteUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeMonthUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeSecondUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeWeekUnit;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.ArrayRangeYearUnit;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DateDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HourCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HourFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.SecondsSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksSub;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsSub;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.Interval;
+import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
+import org.apache.doris.nereids.types.TinyIntType;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class DatetimeFunctionBinderTest {
+
+ private final SlotReference yearUnit = new SlotReference("year",
TinyIntType.INSTANCE);
+ private final SlotReference monthUnit = new SlotReference("month",
TinyIntType.INSTANCE);
+ private final SlotReference weekUnit = new SlotReference("week",
TinyIntType.INSTANCE);
+ private final SlotReference dayUnit = new SlotReference("day",
TinyIntType.INSTANCE);
+ private final SlotReference hourUnit = new SlotReference("hour",
TinyIntType.INSTANCE);
+ private final SlotReference minuteUnit = new SlotReference("minute",
TinyIntType.INSTANCE);
+ private final SlotReference secondUnit = new SlotReference("second",
TinyIntType.INSTANCE);
+ private final SlotReference invalidUnit = new SlotReference("xyz",
TinyIntType.INSTANCE);
+
+ private final TinyIntLiteral tinyIntLiteral = new TinyIntLiteral((byte) 1);
+
+ private final Interval yearInterval = new Interval(tinyIntLiteral, "YEAR");
+ private final Interval quarterInterval = new Interval(tinyIntLiteral,
"QUARTER");
+ private final Interval monthInterval = new Interval(tinyIntLiteral,
"MONTH");
+ private final Interval weekInterval = new Interval(tinyIntLiteral, "WEEK");
+ private final Interval dayInterval = new Interval(tinyIntLiteral, "DAY");
+ private final Interval hourInterval = new Interval(tinyIntLiteral, "HOUR");
+ private final Interval minuteInterval = new Interval(tinyIntLiteral,
"MINUTE");
+ private final Interval secondInterval = new Interval(tinyIntLiteral,
"SECOND");
+
+ private final DateTimeV2Literal dateTimeV2Literal1 = new
DateTimeV2Literal("2024-12-01");
+ private final DateTimeV2Literal dateTimeV2Literal2 = new
DateTimeV2Literal("2024-12-26");
+
+ @Test
+ void testTimestampDiff() {
+ Expression result;
+ UnboundFunction timeDiff;
+ ImmutableList<String> functionNames =
ImmutableList.of("timestampdiff", "datediff");
+
+ for (String functionName : functionNames) {
+ timeDiff = new UnboundFunction(functionName, ImmutableList.of(
+ yearUnit, dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(YearsDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(1));
+
+ timeDiff = new UnboundFunction(functionName, ImmutableList.of(
+ monthUnit, dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(MonthsDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(1));
+
+ timeDiff = new UnboundFunction(functionName, ImmutableList.of(
+ weekUnit, dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(WeeksDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(1));
+
+ timeDiff = new UnboundFunction(functionName, ImmutableList.of(
+ dayUnit, dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(DaysDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(1));
+
+ timeDiff = new UnboundFunction(functionName, ImmutableList.of(
+ hourUnit, dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(HoursDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(1));
+
+ timeDiff = new UnboundFunction(functionName, ImmutableList.of(
+ minuteUnit, dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(MinutesDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(1));
+
+ timeDiff = new UnboundFunction(functionName, ImmutableList.of(
+ secondUnit, dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(SecondsDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(1));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(invalidUnit,
+ dateTimeV2Literal1, dateTimeV2Literal2))));
+
+ if (functionName.equalsIgnoreCase("datediff")) {
+ timeDiff = new UnboundFunction(functionName,
ImmutableList.of(dateTimeV2Literal1, dateTimeV2Literal2));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeDiff);
+ Assertions.assertInstanceOf(DateDiff.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ } else {
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(yearUnit,
+ dateTimeV2Literal1))));
+ }
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(new UnboundSlot("unbound"),
+ dateTimeV2Literal1, dateTimeV2Literal2))));
+ }
+ }
+
+ @Test
+ void testTimestampAdd() {
+ Expression result;
+ UnboundFunction timeAdd;
+ ImmutableList<String> functionNames = ImmutableList.of("timestampadd",
"dateadd");
+
+ for (String functionName : functionNames) {
+ timeAdd = new UnboundFunction(functionName, ImmutableList.of(
+ yearUnit, tinyIntLiteral, dateTimeV2Literal1));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeAdd);
+ Assertions.assertInstanceOf(YearsAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ timeAdd = new UnboundFunction(functionName, ImmutableList.of(
+ monthUnit, tinyIntLiteral, dateTimeV2Literal1));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeAdd);
+ Assertions.assertInstanceOf(MonthsAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ timeAdd = new UnboundFunction(functionName, ImmutableList.of(
+ weekUnit, tinyIntLiteral, dateTimeV2Literal1));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeAdd);
+ Assertions.assertInstanceOf(WeeksAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ timeAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dayUnit, tinyIntLiteral, dateTimeV2Literal1));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeAdd);
+ Assertions.assertInstanceOf(DaysAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ timeAdd = new UnboundFunction(functionName, ImmutableList.of(
+ hourUnit, tinyIntLiteral, dateTimeV2Literal1));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeAdd);
+ Assertions.assertInstanceOf(HoursAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ timeAdd = new UnboundFunction(functionName, ImmutableList.of(
+ minuteUnit, tinyIntLiteral, dateTimeV2Literal1));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeAdd);
+ Assertions.assertInstanceOf(MinutesAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ timeAdd = new UnboundFunction(functionName, ImmutableList.of(
+ secondUnit, tinyIntLiteral, dateTimeV2Literal1));
+ result = DatetimeFunctionBinder.INSTANCE.bind(timeAdd);
+ Assertions.assertInstanceOf(SecondsAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName, ImmutableList.of(
+ invalidUnit, tinyIntLiteral,
dateTimeV2Literal1))));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(yearUnit,
+ dateTimeV2Literal1))));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(new UnboundSlot("unbound"),
+ tinyIntLiteral, dateTimeV2Literal1))));
+ }
+ }
+
+ @Test
+ void testDateAdd() {
+ Expression result;
+ UnboundFunction dateAdd;
+ ImmutableList<String> functionNames = ImmutableList.of("adddate",
"days_add", "date_add");
+
+ for (String functionName : functionNames) {
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, yearInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(YearsAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, monthInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(MonthsAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, weekInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(WeeksAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dayInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(DaysAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, hourInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(HoursAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, minuteInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(MinutesAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, secondInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(SecondsAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateAdd = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, tinyIntLiteral));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd);
+ Assertions.assertInstanceOf(DaysAdd.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(dateTimeV2Literal1))));
+ }
+ }
+
+ @Test
+ void testDateSub() {
+ Expression result;
+ UnboundFunction dateSub;
+ ImmutableList<String> functionNames = ImmutableList.of("subdate",
"days_sub", "date_sub");
+
+ for (String functionName : functionNames) {
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, yearInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(YearsSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, monthInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(MonthsSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, weekInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(WeeksSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dayInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(DaysSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, hourInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(HoursSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, minuteInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(MinutesSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, secondInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(SecondsSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateSub = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, tinyIntLiteral));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateSub);
+ Assertions.assertInstanceOf(DaysSub.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(dateTimeV2Literal1))));
+ }
+ }
+
+ @Test
+ void testDateCeil() {
+ Expression result;
+ UnboundFunction dateCeil;
+ ImmutableList<String> functionNames = ImmutableList.of("date_ceil");
+
+ for (String functionName : functionNames) {
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, yearInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(YearCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, monthInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(MonthCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, weekInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(WeekCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dayInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(DayCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, hourInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(HourCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, minuteInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(MinuteCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, secondInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(SecondCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateCeil = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, tinyIntLiteral));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateCeil);
+ Assertions.assertInstanceOf(SecondCeil.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(dateTimeV2Literal1))));
+ }
+ }
+
+ @Test
+ void testDateFloor() {
+ Expression result;
+ UnboundFunction dateFloor;
+ ImmutableList<String> functionNames = ImmutableList.of("date_floor");
+
+ for (String functionName : functionNames) {
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, yearInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(YearFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, monthInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(MonthFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, weekInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(WeekFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dayInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(DayFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, hourInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(HourFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, minuteInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(MinuteFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, secondInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(SecondFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ dateFloor = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, tinyIntLiteral));
+ result = DatetimeFunctionBinder.INSTANCE.bind(dateFloor);
+ Assertions.assertInstanceOf(SecondFloor.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of(dateTimeV2Literal1))));
+ }
+ }
+
+ @Test
+ void testArrayRange() {
+ Expression result;
+ UnboundFunction arrayRange;
+ ImmutableList<String> functionNames = ImmutableList.of("array_range",
"sequence");
+
+ for (String functionName : functionNames) {
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dateTimeV2Literal2, yearInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRangeYearUnit.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dateTimeV2Literal2, monthInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRangeMonthUnit.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dateTimeV2Literal2, weekInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRangeWeekUnit.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dateTimeV2Literal2, dayInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRangeDayUnit.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dateTimeV2Literal2, hourInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRangeHourUnit.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dateTimeV2Literal2, minuteInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRangeMinuteUnit.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ dateTimeV2Literal1, dateTimeV2Literal2, secondInterval));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRangeSecondUnit.class, result);
+ Assertions.assertEquals(dateTimeV2Literal1, result.child(0));
+ Assertions.assertEquals(dateTimeV2Literal2, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ tinyIntLiteral, tinyIntLiteral, tinyIntLiteral));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRange.class, result);
+ Assertions.assertEquals(tinyIntLiteral, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+ Assertions.assertEquals(tinyIntLiteral, result.child(2));
+
+ arrayRange = new UnboundFunction(functionName, ImmutableList.of(
+ tinyIntLiteral, tinyIntLiteral));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRange.class, result);
+ Assertions.assertEquals(tinyIntLiteral, result.child(0));
+ Assertions.assertEquals(tinyIntLiteral, result.child(1));
+
+ arrayRange = new UnboundFunction(functionName,
ImmutableList.of(tinyIntLiteral));
+ result = DatetimeFunctionBinder.INSTANCE.bind(arrayRange);
+ Assertions.assertInstanceOf(ArrayRange.class, result);
+ Assertions.assertEquals(tinyIntLiteral, result.child(0));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName,
ImmutableList.of())));
+
+ Assertions.assertThrowsExactly(AnalysisException.class,
+ () -> DatetimeFunctionBinder.INSTANCE.bind(
+ new UnboundFunction(functionName, ImmutableList.of(
+ tinyIntLiteral, tinyIntLiteral,
tinyIntLiteral, tinyIntLiteral))));
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]