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 1a684691b77951..4e94dc553cd8d7 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'; @@ -94,7 +93,6 @@ AND: 'AND'; ANTI: 'ANTI'; APPEND: 'APPEND'; ARRAY: 'ARRAY'; -ARRAY_RANGE: 'ARRAY_RANGE'; AS: 'AS'; ASC: 'ASC'; AT: 'AT'; @@ -181,21 +179,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'; @@ -478,7 +467,6 @@ SCHEMAS: 'SCHEMAS'; SECOND: 'SECOND'; SELECT: 'SELECT'; SEMI: 'SEMI'; -SEQUENCE: 'SEQUENCE'; SERIALIZABLE: 'SERIALIZABLE'; SESSION: 'SESSION'; SESSION_USER: 'SESSION_USER'; @@ -507,7 +495,6 @@ STREAM: 'STREAM'; STREAMING: 'STREAMING'; STRING: 'STRING'; STRUCT: 'STRUCT'; -SUBDATE: 'SUBDATE'; SUM: 'SUM'; SUPERUSER: 'SUPERUSER'; SWITCH: 'SWITCH'; @@ -527,8 +514,6 @@ THAN: 'THAN'; THEN: 'THEN'; TIME: 'TIME'; TIMESTAMP: 'TIMESTAMP'; -TIMESTAMPADD: 'TIMESTAMPADD'; -TIMESTAMPDIFF: 'TIMESTAMPDIFF'; TINYINT: 'TINYINT'; TO: 'TO'; TRANSACTION: 'TRANSACTION'; 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 c1e96b7dfe2c9f..12f266d48b0446 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 @@ -1403,7 +1403,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 @@ -1507,57 +1507,8 @@ valueExpression | left=valueExpression comparisonOperator right=valueExpression #comparison ; -datetimeUnit - :YEAR | MONTH | QUARTER | WEEK | DAY | HOUR | MINUTE | SECOND - ; - primaryExpression - : operator=(BITAND | BITOR | BITXOR) LEFT_PAREN left = valueExpression - COMMA right = valueExpression RIGHT_PAREN #bitOperation - | 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 @@ -1821,7 +1772,6 @@ number nonReserved //--DEFAULT-NON-RESERVED-START : ACTIONS - | ADDDATE | AFTER | AGG_STATE | AGGREGATE @@ -1829,7 +1779,6 @@ nonReserved | ALWAYS | ANALYZED | ARRAY - | ARRAY_RANGE | AT | AUTHORS | AUTO_INCREMENT @@ -1894,21 +1843,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 @@ -2091,7 +2031,6 @@ nonReserved | SECOND | SERIALIZABLE | SET_SESSION_VARIABLE - | SEQUENCE | SESSION | SESSION_USER | SHAPE @@ -2112,7 +2051,6 @@ nonReserved | STREAMING | STRING | STRUCT - | SUBDATE | SUM | TABLES | TASK @@ -2122,8 +2060,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 25c315225d21d4..b8fea4049caabf 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 @@ -85,10 +85,8 @@ 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.BaseTableRefContext; -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; @@ -124,10 +122,6 @@ import org.apache.doris.nereids.DorisParser.CreateWorkloadGroupContext; 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; @@ -348,8 +342,6 @@ import org.apache.doris.nereids.DorisParser.TableSnapshotContext; import org.apache.doris.nereids.DorisParser.TableValuedFunctionContext; import org.apache.doris.nereids.DorisParser.TabletListContext; -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; @@ -439,63 +431,18 @@ 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.QuartersAdd; -import org.apache.doris.nereids.trees.expressions.functions.scalar.QuartersSub; -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; @@ -2349,22 +2296,6 @@ public Expression visitArithmeticUnary(ArithmeticUnaryContext ctx) { }); } - @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, () -> { @@ -2421,210 +2352,6 @@ public Expression visitArithmeticBinary(ArithmeticBinaryContext ctx) { }); } - @Override - public Expression visitTimestampdiff(TimestampdiffContext ctx) { - Expression start = (Expression) visit(ctx.startTimestamp); - Expression end = (Expression) visit(ctx.endTimestamp); - String unit = ctx.unit.getText(); - // TODO: support quarters_diff - 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 ("QUARTER".equalsIgnoreCase(unit)) { - return new QuartersAdd(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 ("QUARTER".equalsIgnoreCase(ctx.unit.getText())) { - return new QuartersAdd(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 ("QUARTER".equalsIgnoreCase(ctx.unit.getText())) { - return new QuartersSub(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().alias("CURRENT_DATE"); 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 0a1d2a788dd14e..102637a2abcd4e 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 @@ * 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 FUNCTION_TO_EXPRESSION = ImmutableMap.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 00000000000000..01a3507b527b6a --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java @@ -0,0 +1,354 @@ +// 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.QuartersAdd; +import org.apache.doris.nereids.trees.expressions.functions.scalar.QuartersSub; +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 TIMESTAMP_DIFF_FUNCTION_NAMES + = ImmutableSet.of("TIMESTAMPDIFF", DATEDIFF); + private static final ImmutableSet TIMESTAMP_ADD_FUNCTION_NAMES + = ImmutableSet.of("TIMESTAMPADD", "DATEADD"); + public static final ImmutableSet TIMESTAMP_SERIES_FUNCTION_NAMES + = ImmutableSet.builder() + .addAll(TIMESTAMP_DIFF_FUNCTION_NAMES) + .addAll(TIMESTAMP_ADD_FUNCTION_NAMES) + .build(); + + private static final ImmutableSet ADD_DATE_FUNCTION_NAMES + = ImmutableSet.of("ADDDATE", "DAYS_ADD", "DATE_ADD"); + private static final ImmutableSet SUB_DATE_FUNCTION_NAMES + = ImmutableSet.of("SUBDATE", "DAYS_SUB", "DATE_SUB"); + private static final ImmutableSet DATE_ADD_SUB_SERIES_FUNCTION_NAMES + = ImmutableSet.builder() + .addAll(ADD_DATE_FUNCTION_NAMES) + .addAll(SUB_DATE_FUNCTION_NAMES) + .build(); + private static final ImmutableSet DATE_FLOOR_FUNCTION_NAMES + = ImmutableSet.of("DATE_FLOOR"); + private static final ImmutableSet DATE_CEIL_FUNCTION_NAMES + = ImmutableSet.of("DATE_CEIL"); + private static final ImmutableSet DATE_FLOOR_CEIL_SERIES_FUNCTION_NAMES + = ImmutableSet.builder() + .addAll(DATE_FLOOR_FUNCTION_NAMES) + .addAll(DATE_CEIL_FUNCTION_NAMES) + .build(); + private static final ImmutableSet DATE_SERIES_FUNCTION_NAMES + = ImmutableSet.builder() + .addAll(DATE_ADD_SUB_SERIES_FUNCTION_NAMES) + .addAll(DATE_FLOOR_CEIL_SERIES_FUNCTION_NAMES) + .build(); + + private static final ImmutableSet ARRAY_RANGE_FUNCTION_NAMES + = ImmutableSet.of("ARRAY_RANGE", "SEQUENCE"); + + private static final ImmutableSet SUPPORT_FUNCTION_NAMES + = ImmutableSet.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 QUARTER: + return new QuartersAdd(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 QUARTER: + return new QuartersSub(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 8056f396cd3d07..17eb67479d003e 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.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; @@ -85,6 +86,7 @@ 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; @@ -369,6 +371,26 @@ public Expression visitUnboundFunction(UnboundFunction unboundFunction, Expressi 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 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); } @@ -385,10 +407,28 @@ public Expression visitUnboundFunction(UnboundFunction unboundFunction, Expressi 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; + } } } @@ -470,11 +510,7 @@ public Expression visitTimestampArithmetic(TimestampArithmetic arithmetic, Expre @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 69a75e001ab9eb..599e903df39ba2 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 @@ */ 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/util/TypeCoercionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java index 1fb108b79fd6a6..5f8be613552c78 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 @@ -28,6 +28,7 @@ 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; @@ -785,6 +786,21 @@ private static Expression castChildren(Expression parent, Expression left, Expre 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 00000000000000..f3539b8733d8a3 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinderTest.java @@ -0,0 +1,642 @@ +// 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.QuartersAdd; +import org.apache.doris.nereids.trees.expressions.functions.scalar.QuartersSub; +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 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 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 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, quarterInterval)); + result = DatetimeFunctionBinder.INSTANCE.bind(dateAdd); + Assertions.assertInstanceOf(QuartersAdd.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 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, quarterInterval)); + result = DatetimeFunctionBinder.INSTANCE.bind(dateSub); + Assertions.assertInstanceOf(QuartersSub.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 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 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 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)))); + } + } +}