This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push: new 8eb6a2a7c1a [enhancement](Nereids) boost characterLiteralTypeCoercion (#42941) (#43092) 8eb6a2a7c1a is described below commit 8eb6a2a7c1ae2d37746c0bc500af1e23f90f5f4f Author: 924060929 <924060...@qq.com> AuthorDate: Mon Nov 4 11:50:44 2024 +0800 [enhancement](Nereids) boost characterLiteralTypeCoercion (#42941) (#43092) cherry pick from #42941 --- .../expressions/functions/SearchSignature.java | 9 +- .../trees/expressions/literal/DateLiteral.java | 75 +++- .../trees/expressions/literal/DateTimeLiteral.java | 75 +++- .../expressions/literal/DateTimeV2Literal.java | 2 +- .../expressions/literal/DecimalV3Literal.java | 13 +- .../nereids/trees/expressions/literal/Result.java | 66 +++ .../expressions/literal/format/AndChecker.java | 45 ++ .../expressions/literal/format/AtLeastChecker.java | 49 ++ .../expressions/literal/format/CharChecker.java | 36 ++ .../expressions/literal/format/CheckResult.java | 49 ++ .../literal/format/CustomCharChecker.java | 39 ++ .../literal/format/DateTimeChecker.java | 137 ++++++ .../expressions/literal/format/DebugChecker.java | 38 ++ .../expressions/literal/format/DigitChecker.java | 45 ++ .../expressions/literal/format/FloatChecker.java | 55 +++ .../expressions/literal/format/FormatChecker.java | 170 +++++++ .../expressions/literal/format/IntegerChecker.java | 43 ++ .../expressions/literal/format/LetterChecker.java | 45 ++ .../expressions/literal/format/OptionChecker.java | 36 ++ .../expressions/literal/format/OrChecker.java | 55 +++ .../expressions/literal/format/StringChecker.java | 44 ++ .../expressions/literal/format/StringInspect.java | 64 +++ .../apache/doris/nereids/types/DecimalV3Type.java | 25 +- .../doris/nereids/util/TypeCoercionUtils.java | 54 ++- .../trees/expressions/literal/DateLiteralTest.java | 16 +- .../expressions/literal/DateTimeLiteralTest.java | 496 +++++++++++---------- .../expressions/literal/FloatLiteralTest.java | 80 ++++ .../expressions/literal/IntegerLiteralTest.java | 64 +++ 28 files changed, 1605 insertions(+), 320 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/SearchSignature.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/SearchSignature.java index 4d6bb658356..5fa426a99a2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/SearchSignature.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/SearchSignature.java @@ -207,13 +207,14 @@ public class SearchSignature { int arity = arguments.size(); for (int i = 0; i < arity; i++) { DataType sigArgType = sig.getArgType(i); - DataType realType = arguments.get(i).getDataType(); + Expression argument = arguments.get(i); + DataType realType = argument.getDataType(); // we need to try to do string literal coercion when search signature. // for example, FUNC_A has two signature FUNC_A(datetime) and FUNC_A(string) // if SQL block is `FUNC_A('2020-02-02 00:00:00')`, we should return signature FUNC_A(datetime). - if (arguments.get(i).isLiteral() && realType.isStringLikeType()) { - realType = TypeCoercionUtils.characterLiteralTypeCoercion(((Literal) arguments.get(i)).getStringValue(), - sigArgType).orElse(arguments.get(i)).getDataType(); + if (!argument.isNullLiteral() && argument.isLiteral() && realType.isStringLikeType()) { + realType = TypeCoercionUtils.characterLiteralTypeCoercion(((Literal) argument).getStringValue(), + sigArgType).orElse(argument).getDataType(); } if (!typePredicate.apply(sigArgType, realType)) { return false; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java index e74e3f42387..c224a294b9f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java @@ -44,6 +44,10 @@ import java.util.function.UnaryOperator; public class DateLiteral extends Literal { public static final String JAVA_DATE_FORMAT = "yyyy-MM-dd"; + public static final Set<Character> punctuations = ImmutableSet.of('!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '-', '+', '=', '_', '{', '}', '[', ']', '|', '\\', ':', ';', '"', '\'', '<', '>', ',', '.', '?', '/', '~', + '`'); + // for cast datetime type to date type. private static final LocalDateTime START_OF_A_DAY = LocalDateTime.of(0, 1, 1, 0, 0, 0); private static final LocalDateTime END_OF_A_DAY = LocalDateTime.of(9999, 12, 31, 23, 59, 59, 999999000); @@ -51,10 +55,6 @@ public class DateLiteral extends Literal { private static final DateLiteral MAX_DATE = new DateLiteral(9999, 12, 31); private static final int[] DAYS_IN_MONTH = new int[] {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - private static final Set<Character> punctuations = ImmutableSet.of('!', '@', '#', '$', '%', '^', '&', '*', '(', ')', - '-', '+', '=', '_', '{', '}', '[', ']', '|', '\\', ':', ';', '"', '\'', '<', '>', ',', '.', '?', '/', '~', - '`'); - protected long year; protected long month; protected long day; @@ -145,7 +145,7 @@ public class DateLiteral extends Literal { return punctuations.contains(c); } - static String normalize(String s) { + static Result<String, AnalysisException> normalize(String s) { // merge consecutive space if (s.contains(" ")) { s = s.replaceAll(" +", " "); @@ -208,7 +208,10 @@ public class DateLiteral extends Literal { sb.append('0').append(c); } } else { - throw new AnalysisException("date/datetime literal [" + s + "] is invalid"); + final String currentString = s; + return Result.err( + () -> new AnalysisException("date/datetime literal [" + currentString + "] is invalid") + ); } i = j; partNumber += 1; @@ -228,7 +231,10 @@ public class DateLiteral extends Literal { } else if (partNumber > 3 && isPunctuation(c)) { sb.append(':'); } else { - throw new AnalysisException("date/datetime literal [" + s + "] is invalid"); + final String currentString = s; + return Result.err( + () -> new AnalysisException("date/datetime literal [" + currentString + "] is invalid") + ); } } else { break; @@ -259,15 +265,33 @@ public class DateLiteral extends Literal { // trim use to remove any blank before zone id or zone offset sb.append(s.substring(i).trim()); - return sb.toString(); + return Result.ok(sb.toString()); } - protected static TemporalAccessor parse(String s) { + /** parseDateLiteral */ + public static Result<DateLiteral, AnalysisException> parseDateLiteral(String s) { + Result<TemporalAccessor, AnalysisException> parseResult = parseDateTime(s); + if (parseResult.isError()) { + return parseResult.cast(); + } + TemporalAccessor dateTime = parseResult.get(); + int year = DateUtils.getOrDefault(dateTime, ChronoField.YEAR); + int month = DateUtils.getOrDefault(dateTime, ChronoField.MONTH_OF_YEAR); + int day = DateUtils.getOrDefault(dateTime, ChronoField.DAY_OF_MONTH); + + if (checkDatetime(dateTime) || checkRange(year, month, day) || checkDate(year, month, day)) { + return Result.err(() -> new AnalysisException("date/datetime literal [" + s + "] is out of range")); + } + return Result.ok(new DateLiteral(year, month, day)); + } + + /** parseDateTime */ + public static Result<TemporalAccessor, AnalysisException> parseDateTime(String s) { // fast parse '2022-01-01' if (s.length() == 10 && s.charAt(4) == '-' && s.charAt(7) == '-') { TemporalAccessor date = fastParseDate(s); if (date != null) { - return date; + return Result.ok(date); } } @@ -289,15 +313,20 @@ public class DateLiteral extends Literal { if (!containsPunctuation) { s = normalizeBasic(s); // mysql reject "20200219 010101" "200219 010101", can't use ' ' spilt basic date time. + if (!s.contains("T")) { dateTime = DateTimeFormatterUtils.BASIC_FORMATTER_WITHOUT_T.parse(s); } else { dateTime = DateTimeFormatterUtils.BASIC_DATE_TIME_FORMATTER.parse(s); } - return dateTime; + return Result.ok(dateTime); } - s = normalize(s); + Result<String, AnalysisException> normalizeResult = normalize(s); + if (normalizeResult.isError()) { + return normalizeResult.cast(); + } + s = normalizeResult.get(); if (!s.contains(" ")) { dateTime = DateTimeFormatterUtils.ZONE_DATE_FORMATTER.parse(s); @@ -307,33 +336,35 @@ public class DateLiteral extends Literal { // if Year is not present, throw exception if (!dateTime.isSupported(ChronoField.YEAR)) { - throw new AnalysisException("date/datetime literal [" + originalString + "] is invalid"); + return Result.err( + () -> new AnalysisException("date/datetime literal [" + originalString + "] is invalid") + ); } - return dateTime; + return Result.ok(dateTime); } catch (Exception ex) { - throw new AnalysisException("date/datetime literal [" + originalString + "] is invalid"); + return Result.err(() -> new AnalysisException("date/datetime literal [" + originalString + "] is invalid")); } } protected void init(String s) throws AnalysisException { - TemporalAccessor dateTime = parse(s); + TemporalAccessor dateTime = parseDateTime(s).get(); year = DateUtils.getOrDefault(dateTime, ChronoField.YEAR); month = DateUtils.getOrDefault(dateTime, ChronoField.MONTH_OF_YEAR); day = DateUtils.getOrDefault(dateTime, ChronoField.DAY_OF_MONTH); - if (checkDatetime(dateTime) || checkRange() || checkDate()) { + if (checkDatetime(dateTime) || checkRange(year, month, day) || checkDate(year, month, day)) { throw new AnalysisException("date/datetime literal [" + s + "] is out of range"); } } - protected boolean checkRange() { + protected static boolean checkRange(long year, long month, long day) { return year > MAX_DATE.getYear() || month > MAX_DATE.getMonth() || day > MAX_DATE.getDay(); } - protected boolean checkDate() { - if (month != 0 && day > DAYS_IN_MONTH[((int) month)]) { - if (month == 2 && day == 29 && Year.isLeap(year)) { + protected static boolean checkDate(long year, long month, long day) { + if (month != 0 && day > DAYS_IN_MONTH[(int) month]) { + if (month == 2 && day == 29 && (Year.isLeap(year) && year > 0)) { return false; } return true; @@ -345,7 +376,7 @@ public class DateLiteral extends Literal { return dateTime == null || dateTime.isBefore(START_OF_A_DAY) || dateTime.isAfter(END_OF_A_DAY); } - private boolean checkDatetime(TemporalAccessor dateTime) { + private static boolean checkDatetime(TemporalAccessor dateTime) { return DateUtils.getOrDefault(dateTime, ChronoField.HOUR_OF_DAY) != 0 || DateUtils.getOrDefault(dateTime, ChronoField.MINUTE_OF_HOUR) != 0 || DateUtils.getOrDefault(dateTime, ChronoField.SECOND_OF_MINUTE) != 0 diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java index 726c39c8bb9..27470187eae 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java @@ -23,6 +23,7 @@ import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DateTimeType; +import org.apache.doris.nereids.types.DateTimeV2Type; import org.apache.doris.nereids.types.coercion.DateLikeType; import org.apache.doris.nereids.util.DateUtils; @@ -108,7 +109,7 @@ public class DateTimeLiteral extends DateLiteral { if (s.indexOf("-") == s.lastIndexOf("-") && s.indexOf(":") == s.lastIndexOf(":")) { return 0; } - s = normalize(s); + s = normalize(s).get(); if (s.length() <= 19 || s.charAt(19) != '.') { return 0; } @@ -130,10 +131,73 @@ public class DateTimeLiteral extends DateLiteral { return scale; } - @Override + /** parseDateTimeLiteral */ + public static Result<DateTimeLiteral, AnalysisException> parseDateTimeLiteral(String s, boolean isV2) { + Result<TemporalAccessor, AnalysisException> parseResult = parseDateTime(s); + if (parseResult.isError()) { + return parseResult.cast(); + } + + TemporalAccessor temporal = parseResult.get(); + long year = DateUtils.getOrDefault(temporal, ChronoField.YEAR); + long month = DateUtils.getOrDefault(temporal, ChronoField.MONTH_OF_YEAR); + long day = DateUtils.getOrDefault(temporal, ChronoField.DAY_OF_MONTH); + long hour = DateUtils.getOrDefault(temporal, ChronoField.HOUR_OF_DAY); + long minute = DateUtils.getOrDefault(temporal, ChronoField.MINUTE_OF_HOUR); + long second = DateUtils.getOrDefault(temporal, ChronoField.SECOND_OF_MINUTE); + + ZoneId zoneId = temporal.query(TemporalQueries.zone()); + if (zoneId != null) { + // get correct DST of that time. + Instant thatTime = ZonedDateTime + .of((int) year, (int) month, (int) day, (int) hour, (int) minute, (int) second, 0, zoneId) + .toInstant(); + + int offset = DateUtils.getTimeZone().getRules().getOffset(thatTime).getTotalSeconds() + - zoneId.getRules().getOffset(thatTime).getTotalSeconds(); + if (offset != 0) { + DateTimeLiteral tempLiteral = new DateTimeLiteral(year, month, day, hour, minute, second); + DateTimeLiteral result = (DateTimeLiteral) tempLiteral.plusSeconds(offset); + second = result.second; + minute = result.minute; + hour = result.hour; + day = result.day; + month = result.month; + year = result.year; + } + } + + long microSecond = DateUtils.getOrDefault(temporal, ChronoField.NANO_OF_SECOND) / 100L; + // Microseconds have 7 digits. + long sevenDigit = microSecond % 10; + microSecond = microSecond / 10; + if (sevenDigit >= 5 && isV2) { + DateTimeV2Literal tempLiteral = new DateTimeV2Literal(year, month, day, hour, minute, second, microSecond); + DateTimeV2Literal result = (DateTimeV2Literal) tempLiteral.plusMicroSeconds(1); + second = result.second; + minute = result.minute; + hour = result.hour; + day = result.day; + month = result.month; + year = result.year; + microSecond = result.microSecond; + } + + if (checkRange(year, month, day) || checkDate(year, month, day)) { + return Result.err(() -> new AnalysisException("datetime literal [" + s + "] is out of range")); + } + + if (isV2) { + DateTimeV2Type type = DateTimeV2Type.forTypeFromString(s); + return Result.ok(new DateTimeV2Literal(type, year, month, day, hour, minute, second, microSecond)); + } else { + return Result.ok(new DateTimeLiteral(DateTimeType.INSTANCE, year, month, day, hour, minute, second)); + } + } + protected void init(String s) throws AnalysisException { // TODO: check and do fast parse like fastParseDate - TemporalAccessor temporal = parse(s); + TemporalAccessor temporal = parseDateTime(s).get(); year = DateUtils.getOrDefault(temporal, ChronoField.YEAR); month = DateUtils.getOrDefault(temporal, ChronoField.MONTH_OF_YEAR); @@ -177,14 +241,13 @@ public class DateTimeLiteral extends DateLiteral { this.microSecond = result.microSecond; } - if (checkRange() || checkDate()) { + if (checkRange(year, month, day) || checkDate(year, month, day)) { throw new AnalysisException("datetime literal [" + s + "] is out of range"); } } - @Override protected boolean checkRange() { - return super.checkRange() || hour > MAX_DATETIME.getHour() || minute > MAX_DATETIME.getMinute() + return checkRange(year, month, day) || hour > MAX_DATETIME.getHour() || minute > MAX_DATETIME.getMinute() || second > MAX_DATETIME.getSecond() || microSecond > MAX_MICROSECOND; } 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 0ca19bf2a92..c6ec4b99d4f 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 @@ -77,7 +77,7 @@ public class DateTimeV2Literal extends DateTimeLiteral { this.second = localDateTime.getSecond(); this.microSecond -= 1000000; } - if (checkRange() || checkDate()) { + if (checkRange() || checkDate(year, month, day)) { // may fallback to legacy planner. make sure the behaviour of rounding is same. throw new AnalysisException("datetime literal [" + toString() + "] is out of range"); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java index 1ff2e50169c..d8be4faf0c9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java @@ -44,11 +44,16 @@ public class DecimalV3Literal extends FractionalLiteral { * Constructor for DecimalV3Literal */ public DecimalV3Literal(DecimalV3Type dataType, BigDecimal value) { - super(DecimalV3Type.createDecimalV3TypeLooseCheck(dataType.getPrecision(), dataType.getScale())); + super(DecimalV3Type.createDecimalV3TypeLooseCheck( + dataType.getPrecision() == -1 ? value.precision() : dataType.getPrecision(), + dataType.getScale() == -1 ? value.scale() : dataType.getScale()) + ); + + int precision = dataType.getPrecision() == -1 ? value.precision() : dataType.getPrecision(); + int scale = dataType.getScale() == -1 ? value.scale() : dataType.getScale(); Objects.requireNonNull(value, "value not be null"); - checkPrecisionAndScale(dataType.getPrecision(), dataType.getScale(), value); - BigDecimal adjustedValue = value.scale() < 0 ? value - : value.setScale(dataType.getScale(), RoundingMode.HALF_UP); + checkPrecisionAndScale(precision, scale, value); + BigDecimal adjustedValue = value.scale() < 0 ? value : value.setScale(scale, RoundingMode.HALF_UP); this.value = Objects.requireNonNull(adjustedValue); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Result.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Result.java new file mode 100644 index 00000000000..aa5040abcf4 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Result.java @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal; + +import java.util.Optional; +import java.util.function.Supplier; + +/** Result */ +public class Result<R, T extends RuntimeException> { + private final Optional<R> result; + private final Optional<Supplier<T>> exceptionSupplier; + + private Result(Optional<R> result, Optional<Supplier<T>> exceptionSupplier) { + this.result = result; + this.exceptionSupplier = exceptionSupplier; + } + + public static <R, T extends RuntimeException> Result<R, T> ok(R result) { + return new Result<>(Optional.of(result), Optional.empty()); + } + + public static <R, T extends RuntimeException> Result<R, T> err(Supplier<T> exceptionSupplier) { + return new Result<>(Optional.empty(), Optional.of(exceptionSupplier)); + } + + public boolean isOk() { + return !exceptionSupplier.isPresent(); + } + + public boolean isError() { + return exceptionSupplier.isPresent(); + } + + public <R, T extends RuntimeException> Result<R, T> cast() { + return (Result<R, T>) this; + } + + public R get() { + if (exceptionSupplier.isPresent()) { + throw exceptionSupplier.get().get(); + } + return result.get(); + } + + public R orElse(R other) { + if (exceptionSupplier.isPresent()) { + return other; + } + return result.get(); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/AndChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/AndChecker.java new file mode 100644 index 00000000000..377d8b2202b --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/AndChecker.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import org.apache.doris.nereids.util.Utils; + +import java.util.List; +import java.util.Objects; + +/** AndChecker */ +public class AndChecker extends FormatChecker { + private final List<FormatChecker> checkers; + + public AndChecker(String name, List<FormatChecker> checkers) { + super(name); + this.checkers = Utils.fastToImmutableList( + Objects.requireNonNull(checkers, "checkers can not be null") + ); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + for (FormatChecker checker : checkers) { + if (!checker.check(stringInspect).matched) { + return false; + } + } + return true; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/AtLeastChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/AtLeastChecker.java new file mode 100644 index 00000000000..f69c942d966 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/AtLeastChecker.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import java.util.Objects; +import java.util.function.Predicate; + +/** AtLeastChecker */ +public class AtLeastChecker extends FormatChecker { + private final int minCount; + private final int maxRead; + private final Predicate<Character> checker; + + public AtLeastChecker(String name, int minCount, int maxRead, Predicate<Character> checker) { + super(name); + this.minCount = minCount; + this.maxRead = maxRead; + this.checker = Objects.requireNonNull(checker, "checker can not be null"); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + int count = 0; + boolean checkRead = maxRead >= 0; + while (!stringInspect.eos() && (!checkRead || count < maxRead)) { + if (!checker.test(stringInspect.lookAt())) { + break; + } + stringInspect.step(); + count++; + } + return count >= minCount; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CharChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CharChecker.java new file mode 100644 index 00000000000..14934bcb846 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CharChecker.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +/** CharChecker */ +public class CharChecker extends FormatChecker { + public final char c; + + public CharChecker(String name, char c) { + super(name); + this.c = c; + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + if (stringInspect.eos() || stringInspect.lookAndStep() != c) { + return false; + } + return true; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CheckResult.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CheckResult.java new file mode 100644 index 00000000000..b604a3516ab --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CheckResult.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +/** CheckResult */ +public class CheckResult { + public final FormatChecker checker; + public final StringInspect stringInspect; + public final boolean matched; + public final int checkStartIndex; + public final int checkEndIndex; + + public CheckResult( + FormatChecker checker, StringInspect stringInspect, + boolean matched, int checkStartIndex, int checkEndIndex) { + this.checker = checker; + this.stringInspect = stringInspect; + this.matched = matched; + this.checkStartIndex = checkStartIndex; + this.checkEndIndex = checkEndIndex; + } + + public int matchesLength() { + return checkEndIndex - checkStartIndex; + } + + public String matchesContent() { + return stringInspect.str.substring(checkStartIndex, checkEndIndex); + } + + public void stepBack() { + stringInspect.setIndex(checkStartIndex); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CustomCharChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CustomCharChecker.java new file mode 100644 index 00000000000..0718202bf03 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/CustomCharChecker.java @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import java.util.Objects; +import java.util.function.Predicate; + +/** CustomCharChecker */ +public class CustomCharChecker extends FormatChecker { + private final Predicate<Character> checker; + + public CustomCharChecker(String name, Predicate<Character> checker) { + super(name); + this.checker = Objects.requireNonNull(checker, "checker can not be null"); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + if (stringInspect.eos() || !checker.test(stringInspect.lookAndStep())) { + return false; + } + return true; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DateTimeChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DateTimeChecker.java new file mode 100644 index 00000000000..7d60a01c041 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DateTimeChecker.java @@ -0,0 +1,137 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import org.apache.doris.nereids.trees.expressions.literal.DateLiteral; + +/** DateTimeChecker */ +public class DateTimeChecker extends FormatChecker { + private static final DateTimeChecker INSTANCE = new DateTimeChecker(); + + private final FormatChecker checker; + + private DateTimeChecker() { + super("DateTimeChecker"); + + this.checker = + or( + // date + and("Date", + or( + // 20241012 + digit(8, 8), + // 2024-10-12 + and( + digit(1, 4), // year + chars(DateLiteral.punctuations::contains), + digit(1, 2), // month + chars(DateLiteral.punctuations::contains), + digit(1, 2) // day + ) + ) + ), + // datetime + and("DateTime", + or("YearToSecond", + // 20241012010203 + and("FullCompactDateTime", + digit(8, 8), + atLeast(0, c -> c == 'T'), + digit(6, 6) + ), + + // 241012010203 or 241012T010203 + and("ShortCompactDateTime", + digit(6, 6), + atLeast(0, c -> c == 'T'), + digit(6, 6) + ), + + // 2024-01-01 01:02:03 + and("NormalDateTime", + digit(1, 4), // year + chars(DateLiteral.punctuations::contains), + digit(1, 2), // month + chars(DateLiteral.punctuations::contains), + digit(1, 2), // day + atLeast(1, c -> c == 'T' || c == ' ' || DateLiteral.punctuations.contains(c)), + digit(1, 2), // hour + option( + and( + chars(DateLiteral.punctuations::contains), + digit(1, 2), // minute + option( + and( + chars(DateLiteral.punctuations::contains), + digit(1, 2) // second + ) + ) + ) + ) + ) + ), + option("NanoSecond", nanoSecond()), + option("TimeZone", timeZone()) + ) + ); + } + + public static boolean isValidDateTime(String str) { + str = str.trim(); + StringInspect stringInspect = new StringInspect(str.trim()); + return INSTANCE.check(stringInspect).matched && stringInspect.eos(); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + return checker.check(stringInspect).matched; + } + + private FormatChecker nanoSecond() { + return and( + ch('.'), + digit(1) + ); + } + + private FormatChecker timeZone() { + // Z or +08:00 or UTC-01:00 + return and( + // timezone: Europe/London, America/New_York + atLeast(0, c -> ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '/' || c == '_'), + option( + and( + chars(c -> c == '+' || c == '-'), + digit(1, 2), + option( + and( + ch(':'), + digit(1, 2), + option( + and( + ch(':'), + digit(1, 2) + ) + ) + ) + ) + ) + ) + ); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DebugChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DebugChecker.java new file mode 100644 index 00000000000..6569b914d5e --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DebugChecker.java @@ -0,0 +1,38 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import java.util.Objects; +import java.util.function.Predicate; + +/** DebugChecker */ +public class DebugChecker<T extends FormatChecker> extends FormatChecker { + private final T childChecker; + private final Predicate<T> debugPoint; + + public DebugChecker(String name, T childChecker, Predicate<T> debugPoint) { + super(name); + this.childChecker = Objects.requireNonNull(childChecker, "childChecker can not be null"); + this.debugPoint = Objects.requireNonNull(debugPoint, "debugPoint can not be null"); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + return debugPoint.test(childChecker); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DigitChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DigitChecker.java new file mode 100644 index 00000000000..fe1cb26fda4 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/DigitChecker.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +/** DigitChecker */ +public class DigitChecker extends FormatChecker { + private final int minCount; + private final int maxRead; + + public DigitChecker(String name, int minCount, int maxRead) { + super(name); + this.minCount = minCount; + this.maxRead = maxRead; + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + int numberCount = 0; + boolean checkRead = maxRead >= 0; + while (!stringInspect.eos() && (!checkRead || numberCount < maxRead)) { + char c = stringInspect.lookAt(); + if (!('0' <= c && c <= '9')) { + break; + } + stringInspect.step(); + numberCount++; + } + return numberCount >= minCount; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/FloatChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/FloatChecker.java new file mode 100644 index 00000000000..9b438f45324 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/FloatChecker.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +/** FloatChecker */ +public class FloatChecker extends FormatChecker { + private static final FloatChecker INSTANCE = new FloatChecker(); + private final FormatChecker checker; + + private FloatChecker() { + super("FloatChecker"); + checker = and( + option(chars(c -> c == '+' || c == '-')), + or( + // 123 or 123.456 + and(digit(1), option(and(ch('.'), digit(0)))), + // .123 + and(ch('.'), digit(1)) + ), + option( + // E+10 or E-10 or E10 + and( + chars(c -> c == 'e' || c == 'E'), + option(chars(c -> c == '+' || c == '-')), + digit(1) + ) + ) + ); + } + + public static boolean isValidFloat(String str) { + StringInspect stringInspect = new StringInspect(str.trim()); + return INSTANCE.check(stringInspect).matched && stringInspect.eos(); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + return checker.check(stringInspect).matched; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/FormatChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/FormatChecker.java new file mode 100644 index 00000000000..fc6d2acc993 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/FormatChecker.java @@ -0,0 +1,170 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import org.apache.doris.nereids.util.Utils; + +import java.util.Objects; +import java.util.function.Predicate; + +/** + * FormatChecker + * + * This class is used to check whether the string satisfy the underscore format(like DSL), without throw Exception. + * For example, to check whether the string can be converted to integer, you can use below format, it can + * check "1", "-123", and "-143". More complex example is DateTimeChecker + * + * <pre> + * FormatChecker checker = and( + * option(chars(c -> c == '+' || c == '-')), + * digit(1) + * ); + * + * checker.check(new StringInspector("+12345")) + * </pre> + */ +public abstract class FormatChecker { + public final String name; + + public FormatChecker(String name) { + this.name = Objects.requireNonNull(name, "name can not be null"); + } + + protected abstract boolean doCheck(StringInspect stringInspect); + + /** check */ + public final CheckResult check(StringInspect stringInspect) { + int checkStartIndex = stringInspect.index(); + boolean matches = doCheck(stringInspect); + int checkEndIndex = stringInspect.index(); + CheckResult checkResult = new CheckResult(this, stringInspect, matches, checkStartIndex, checkEndIndex); + if (!matches) { + checkResult.stepBack(); + } + return checkResult; + } + + protected <T extends FormatChecker> DebugChecker<T> debug(T childChecker, Predicate<T> debugPoint) { + return debug("DebugChecker", childChecker, debugPoint); + } + + protected <T extends FormatChecker> DebugChecker<T> debug(String name, T childChecker, Predicate<T> debugPoint) { + return new DebugChecker<>(name, childChecker, debugPoint); + } + + protected OptionChecker option(FormatChecker checker) { + return option("OptionChecker", checker); + } + + protected OptionChecker option(String name, FormatChecker checker) { + return new OptionChecker(name, checker); + } + + protected AndChecker and(FormatChecker... checkers) { + return and("AndChecker", checkers); + } + + protected AndChecker and(String name, FormatChecker... checkers) { + return new AndChecker(name, Utils.fastToImmutableList(checkers)); + } + + protected OrChecker or(FormatChecker... checkers) { + return or("OrChecker", checkers); + } + + protected OrChecker or(String name, FormatChecker... checkers) { + return new OrChecker(name, Utils.fastToImmutableList(checkers)); + } + + protected AtLeastChecker atLeast(int minCount, Predicate<Character> checker) { + return atLeast("AtLeastChecker", minCount, -1, checker); + } + + protected AtLeastChecker atLeast(String name, int minCount, Predicate<Character> checker) { + return new AtLeastChecker(name, minCount, -1, checker); + } + + protected AtLeastChecker atLeast(int minCount, int maxRead, Predicate<Character> checker) { + return atLeast("AtLeastChecker", minCount, maxRead, checker); + } + + protected AtLeastChecker atLeast(String name, int minCount, int maxRead, Predicate<Character> checker) { + return new AtLeastChecker(name, minCount, maxRead, checker); + } + + protected DigitChecker digit(int minCount) { + return digit("DigitChecker", minCount, -1); + } + + protected DigitChecker digit(String name, int minCount) { + return new DigitChecker(name, minCount, -1); + } + + protected DigitChecker digit(int minCount, int maxRead) { + return digit("DigitChecker", minCount, maxRead); + } + + protected DigitChecker digit(String name, int minCount, int maxRead) { + return new DigitChecker(name, minCount, maxRead); + } + + protected LetterChecker letter(int minCount) { + return letter("LetterChecker", minCount, -1); + } + + protected LetterChecker letter(String name, int minCount) { + return new LetterChecker(name, minCount, -1); + } + + protected LetterChecker letter(int minCount, int maxRead) { + return letter("LetterChecker", minCount, maxRead); + } + + protected LetterChecker letter(String name, int minCount, int maxRead) { + return new LetterChecker(name, minCount, maxRead); + } + + protected CharChecker ch(char c) { + return ch("CharChecker", c); + } + + protected CharChecker ch(String name, char c) { + return new CharChecker(name, c); + } + + protected CustomCharChecker chars(Predicate<Character> checker) { + return chars("CustomCharChecker", checker); + } + + protected CustomCharChecker chars(String name, Predicate<Character> checker) { + return new CustomCharChecker(name, checker); + } + + protected StringChecker string(String equalsTo) { + return string("StringChecker", equalsTo); + } + + protected StringChecker string(String name, String equalsTo) { + return new StringChecker(name, equalsTo); + } + + @Override + public String toString() { + return name; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/IntegerChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/IntegerChecker.java new file mode 100644 index 00000000000..54efc875705 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/IntegerChecker.java @@ -0,0 +1,43 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +/** IntegerChecker */ +public class IntegerChecker extends FormatChecker { + private static final IntegerChecker INSTANCE = new IntegerChecker(); + + private final FormatChecker checker; + + private IntegerChecker() { + super("IntegerChecker"); + checker = and( + option(chars(c -> c == '+' || c == '-')), + digit(1) + ); + } + + public static boolean isValidInteger(String string) { + StringInspect stringInspect = new StringInspect(string); + return INSTANCE.check(stringInspect).matched && stringInspect.eos(); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + return checker.check(stringInspect).matched; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/LetterChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/LetterChecker.java new file mode 100644 index 00000000000..d4ba3131608 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/LetterChecker.java @@ -0,0 +1,45 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +/** LetterChecker */ +public class LetterChecker extends FormatChecker { + private final int minCount; + private final int maxRead; + + public LetterChecker(String name, int minCount, int maxRead) { + super(name); + this.minCount = minCount; + this.maxRead = maxRead; + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + int numberCount = 0; + boolean checkRead = maxRead >= 0; + while (!stringInspect.eos() && (!checkRead || numberCount < maxRead)) { + char c = stringInspect.lookAt(); + if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))) { + break; + } + stringInspect.step(); + numberCount++; + } + return numberCount >= minCount; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/OptionChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/OptionChecker.java new file mode 100644 index 00000000000..0946c95fefc --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/OptionChecker.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import java.util.Objects; + +/** OptionChecker */ +public class OptionChecker extends FormatChecker { + private final FormatChecker checker; + + public OptionChecker(String name, FormatChecker checker) { + super(name); + this.checker = Objects.requireNonNull(checker, "checker can not be null"); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + checker.check(stringInspect); + return true; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/OrChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/OrChecker.java new file mode 100644 index 00000000000..dbf109d8193 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/OrChecker.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import org.apache.doris.nereids.util.Utils; + +import java.util.List; +import java.util.Objects; + +/** OrChecker */ +public class OrChecker extends FormatChecker { + private final List<FormatChecker> checkers; + + public OrChecker(String name, List<FormatChecker> checkers) { + super(name); + this.checkers = Utils.fastToImmutableList( + Objects.requireNonNull(checkers, "checkers can not be null") + ); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + int maxMatches = -1; + CheckResult maxMatchesResult = null; + for (FormatChecker checker : checkers) { + CheckResult checkResult = checker.check(stringInspect); + if (checkResult.matched && checkResult.matchesLength() > maxMatches) { + maxMatches = checkResult.matchesLength(); + maxMatchesResult = checkResult; + } + checkResult.stepBack(); + } + if (maxMatches >= 0) { + stringInspect.setIndex(maxMatchesResult.checkEndIndex); + return true; + } else { + return false; + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/StringChecker.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/StringChecker.java new file mode 100644 index 00000000000..e1bf9e0f9b5 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/StringChecker.java @@ -0,0 +1,44 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import java.util.Objects; + +/** StringChecker */ +public class StringChecker extends FormatChecker { + private final String str; + + public StringChecker(String name, String str) { + super(name); + this.str = Objects.requireNonNull(str, "str can not be null"); + } + + @Override + protected boolean doCheck(StringInspect stringInspect) { + if (stringInspect.remain() < str.length()) { + return false; + } + + for (int i = 0; i < str.length(); i++) { + if (stringInspect.lookAndStep() != str.charAt(i)) { + return false; + } + } + return true; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/StringInspect.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/StringInspect.java new file mode 100644 index 00000000000..fab366b6078 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/format/StringInspect.java @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal.format; + +import java.util.Objects; + +/** + * StringInspect, a simple lexer can save and move the index + */ +public class StringInspect { + public final String str; + private int index; + + public StringInspect(String str) { + this.str = Objects.requireNonNull(str, "str cannot be null"); + } + + public boolean eos() { + return index >= str.length(); + } + + public int remain() { + return str.length() - index; + } + + public char lookAt() { + return str.charAt(index); + } + + public void step() { + this.index++; + } + + public void step(int step) { + this.index += step; + } + + public void setIndex(int index) { + this.index = index; + } + + public char lookAndStep() { + return str.charAt(index++); + } + + public int index() { + return index; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java index aaef3775b34..3dd2ae35c3e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java @@ -20,6 +20,7 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.ScalarType; import org.apache.doris.catalog.Type; import org.apache.doris.nereids.annotation.Developing; +import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.exceptions.NotSupportedException; import org.apache.doris.nereids.types.coercion.FractionalType; import org.apache.doris.qe.ConnectContext; @@ -136,15 +137,25 @@ public class DecimalV3Type extends FractionalType { enableDecimal256 = connectContext.getSessionVariable().isEnableDecimal256(); } if (enableDecimal256) { - Preconditions.checkArgument(precision > 0 && precision <= MAX_DECIMAL256_PRECISION, - "precision should in (0, " + MAX_DECIMAL256_PRECISION + "], but real precision is " + precision); + if (!(precision > 0 && precision <= MAX_DECIMAL256_PRECISION)) { + throw new AnalysisException( + "precision should in (0, " + MAX_DECIMAL256_PRECISION + "], but real precision is " + precision + ); + } } else { - Preconditions.checkArgument(precision > 0 && precision <= MAX_DECIMAL128_PRECISION, - "precision should in (0, " + MAX_DECIMAL128_PRECISION + "], but real precision is " + precision); + if (!(precision > 0 && precision <= MAX_DECIMAL128_PRECISION)) { + throw new AnalysisException( + "precision should in (0, " + MAX_DECIMAL128_PRECISION + "], but real precision is " + precision + ); + } + } + if (scale < 0) { + throw new AnalysisException("scale should not smaller than 0, but real scale is " + scale); + } + if (precision < scale) { + throw new AnalysisException("precision should not smaller than scale," + + " but precision is " + precision + ", scale is " + scale); } - Preconditions.checkArgument(scale >= 0, "scale should not smaller than 0, but real scale is " + scale); - Preconditions.checkArgument(precision >= scale, "precision should not smaller than scale," - + " but precision is " + precision, ", scale is " + scale); return new DecimalV3Type(precision, scale); } 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 651e018c20e..3acf4508ac9 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 @@ -66,11 +66,15 @@ import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.literal.MapLiteral; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.trees.expressions.literal.Result; import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; +import org.apache.doris.nereids.trees.expressions.literal.format.DateTimeChecker; +import org.apache.doris.nereids.trees.expressions.literal.format.FloatChecker; +import org.apache.doris.nereids.trees.expressions.literal.format.IntegerChecker; import org.apache.doris.nereids.types.ArrayType; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.types.BooleanType; @@ -534,7 +538,7 @@ public class TypeCoercionUtils { if ("false".equalsIgnoreCase(value)) { ret = BooleanLiteral.FALSE; } - } else if (dataType instanceof TinyIntType) { + } else if (dataType instanceof TinyIntType && IntegerChecker.isValidInteger(value)) { BigInteger bigInt = new BigInteger(value); if (BigInteger.valueOf(bigInt.byteValue()).equals(bigInt)) { ret = new TinyIntLiteral(bigInt.byteValue()); @@ -547,7 +551,7 @@ public class TypeCoercionUtils { } else { ret = new LargeIntLiteral(bigInt); } - } else if (dataType instanceof SmallIntType) { + } else if (dataType instanceof SmallIntType && IntegerChecker.isValidInteger(value)) { BigInteger bigInt = new BigInteger(value); if (BigInteger.valueOf(bigInt.shortValue()).equals(bigInt)) { ret = new SmallIntLiteral(bigInt.shortValue()); @@ -558,7 +562,7 @@ public class TypeCoercionUtils { } else { ret = new LargeIntLiteral(bigInt); } - } else if (dataType instanceof IntegerType) { + } else if (dataType instanceof IntegerType && IntegerChecker.isValidInteger(value)) { BigInteger bigInt = new BigInteger(value); if (BigInteger.valueOf(bigInt.intValue()).equals(bigInt)) { ret = new IntegerLiteral(bigInt.intValue()); @@ -567,23 +571,23 @@ public class TypeCoercionUtils { } else { ret = new LargeIntLiteral(bigInt); } - } else if (dataType instanceof BigIntType) { + } else if (dataType instanceof BigIntType && IntegerChecker.isValidInteger(value)) { BigInteger bigInt = new BigInteger(value); if (BigInteger.valueOf(bigInt.longValue()).equals(bigInt)) { ret = new BigIntLiteral(bigInt.longValueExact()); } else { ret = new LargeIntLiteral(bigInt); } - } else if (dataType instanceof LargeIntType) { + } else if (dataType instanceof LargeIntType && IntegerChecker.isValidInteger(value)) { BigInteger bigInt = new BigInteger(value); ret = new LargeIntLiteral(bigInt); - } else if (dataType instanceof FloatType) { + } else if (dataType instanceof FloatType && FloatChecker.isValidFloat(value)) { ret = new FloatLiteral(Float.parseFloat(value)); - } else if (dataType instanceof DoubleType) { + } else if (dataType instanceof DoubleType && FloatChecker.isValidFloat(value)) { ret = new DoubleLiteral(Double.parseDouble(value)); - } else if (dataType instanceof DecimalV2Type) { + } else if (dataType instanceof DecimalV2Type && FloatChecker.isValidFloat(value)) { ret = new DecimalLiteral(new BigDecimal(value)); - } else if (dataType instanceof DecimalV3Type) { + } else if (dataType instanceof DecimalV3Type && FloatChecker.isValidFloat(value)) { ret = new DecimalV3Literal((DecimalV3Type) dataType, new BigDecimal(value)); } else if (dataType instanceof CharType) { ret = new VarcharLiteral(value, ((CharType) dataType).getLen()); @@ -591,22 +595,24 @@ public class TypeCoercionUtils { ret = new VarcharLiteral(value, ((VarcharType) dataType).getLen()); } else if (dataType instanceof StringType) { ret = new StringLiteral(value); - } else if (dataType.isDateTimeV2Type()) { - ret = new DateTimeV2Literal(value); - } else if (dataType.isDateTimeType()) { - ret = new DateTimeLiteral(value); - } else if (dataType.isDateV2Type()) { - try { - ret = new DateV2Literal(value); - } catch (AnalysisException e) { - ret = new DateTimeV2Literal(value); - } - } else if (dataType.isDateType()) { - try { - ret = new DateLiteral(value); - } catch (AnalysisException e) { - ret = new DateTimeLiteral(value); + } else if (dataType.isDateTimeV2Type() && DateTimeChecker.isValidDateTime(value)) { + ret = DateTimeLiteral.parseDateTimeLiteral(value, true).orElse(null); + } else if (dataType.isDateTimeType() && DateTimeChecker.isValidDateTime(value)) { + ret = DateTimeLiteral.parseDateTimeLiteral(value, false).orElse(null); + } else if (dataType.isDateV2Type() && DateTimeChecker.isValidDateTime(value)) { + Result<DateLiteral, AnalysisException> parseResult + = DateV2Literal.parseDateLiteral(value); + if (parseResult.isOk()) { + ret = parseResult.get(); + } else { + Result<DateTimeLiteral, AnalysisException> parseResult2 + = DateTimeV2Literal.parseDateTimeLiteral(value, true); + if (parseResult2.isOk()) { + ret = parseResult2.get(); + } } + } else if (dataType.isDateType() && DateTimeChecker.isValidDateTime(value)) { + ret = DateLiteral.parseDateLiteral(value).orElse(null); } } catch (Exception e) { if (LOG.isDebugEnabled()) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java index 7c8ad5ed0e6..8db1c9446d0 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java @@ -36,22 +36,22 @@ class DateLiteralTest { @Test void testNormalize() { - String s = DateLiteral.normalize("2021-5"); + String s = DateLiteral.normalize("2021-5").get(); Assertions.assertEquals("2021-05", s); - s = DateLiteral.normalize("2021-5-1"); + s = DateLiteral.normalize("2021-5-1").get(); Assertions.assertEquals("2021-05-01", s); - s = DateLiteral.normalize("2021-5-01"); + s = DateLiteral.normalize("2021-5-01").get(); Assertions.assertEquals("2021-05-01", s); - s = DateLiteral.normalize("2021-5-01 0:0:0"); + s = DateLiteral.normalize("2021-5-01 0:0:0").get(); Assertions.assertEquals("2021-05-01 00:00:00", s); - s = DateLiteral.normalize("2021-5-01 0:0:0.001"); + s = DateLiteral.normalize("2021-5-01 0:0:0.001").get(); Assertions.assertEquals("2021-05-01 00:00:00.001", s); - s = DateLiteral.normalize("2021-5-01 0:0:0.12345678"); + s = DateLiteral.normalize("2021-5-01 0:0:0.12345678").get(); Assertions.assertEquals("2021-05-01 00:00:00.1234567", s); - s = DateLiteral.normalize("2021-5-1 Asia/Shanghai"); + s = DateLiteral.normalize("2021-5-1 Asia/Shanghai").get(); Assertions.assertEquals("2021-05-01Asia/Shanghai", s); - s = DateLiteral.normalize("2021-5-1 0:0:0.12345678 Asia/Shanghai"); + s = DateLiteral.normalize("2021-5-1 0:0:0.12345678 Asia/Shanghai").get(); Assertions.assertEquals("2021-05-01 00:00:00.1234567Asia/Shanghai", s); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteralTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteralTest.java index 68e2d80826b..54b0ea1e6c3 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteralTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteralTest.java @@ -17,6 +17,7 @@ package org.apache.doris.nereids.trees.expressions.literal; +import org.apache.doris.nereids.trees.expressions.literal.format.DateTimeChecker; import org.apache.doris.nereids.types.DateTimeV2Type; import org.junit.jupiter.api.Assertions; @@ -24,20 +25,22 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.function.Consumer; +import java.util.function.Function; class DateTimeLiteralTest { @Test void reject() { // Assertions.assertThrows(IllegalArgumentException.class, () -> { - // new DateTimeV2Literal("2022-08-01T01:01:01-00:00"); + // check("", DateTimeV2Literal::new"2022-08-01T01:01:01-00:00"); // }); } @Test void mysqlStrangeCase() { - new DateTimeV2Literal("0-08-01 13:21:03"); - new DateTimeV2Literal("0001-01-01: 00:01:01.001"); - new DateTimeV2Literal("2021?01?01 00.00.00"); + check("0-08-01 13:21:03", DateTimeV2Literal::new); + check("0-08-01 13:21:03", DateTimeV2Literal::new); + check("0001-01-01: 00:01:01.001", DateTimeV2Literal::new); + check("2021?01?01 00.00.00", DateTimeV2Literal::new); } @Test @@ -51,43 +54,43 @@ class DateTimeLiteralTest { Assertions.assertEquals(2, datetime.second); }; - assertFunc.accept(new DateTimeV2Literal("20220801010102")); - assertFunc.accept(new DateTimeV2Literal("20220801T010102")); - assertFunc.accept(new DateTimeV2Literal("220801010102")); - assertFunc.accept(new DateTimeV2Literal("220801T010102")); - assertFunc.accept(new DateTimeV2Literal("20220801010101.9999999")); + assertFunc.accept(check("20220801010102", DateTimeV2Literal::new)); + assertFunc.accept(check("20220801T010102", DateTimeV2Literal::new)); + assertFunc.accept(check("220801010102", DateTimeV2Literal::new)); + assertFunc.accept(check("220801T010102", DateTimeV2Literal::new)); + assertFunc.accept(check("20220801010101.9999999", DateTimeV2Literal::new)); } @Test void testMicrosecond() { DateTimeV2Literal literal; - literal = new DateTimeV2Literal("2016-07-02 00:00:00.123"); + literal = check("2016-07-02 00:00:00.123", DateTimeV2Literal::new); Assertions.assertEquals(123000, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 00:00:00.123456"); + literal = check("2016-07-02 00:00:00.123456", DateTimeV2Literal::new); Assertions.assertEquals(123456, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 00:00:00.1"); + literal = check("2016-07-02 00:00:00.1", DateTimeV2Literal::new); Assertions.assertEquals(100000, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 00:00:00.000001"); + literal = check("2016-07-02 00:00:00.000001", DateTimeV2Literal::new); Assertions.assertEquals(1, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 00:00:00.12345"); + literal = check("2016-07-02 00:00:00.12345", DateTimeV2Literal::new); Assertions.assertEquals(123450, literal.microSecond); } @Test void testWithoutZoneOrOffset() { - new DateTimeV2Literal("2022-08-01"); + check("2022-08-01", DateTimeV2Literal::new); - new DateTimeV2Literal("2022-08-01 01:01:01"); - new DateTimeV2Literal("2022-08-01 01:01"); - new DateTimeV2Literal("2022-08-01 01"); + check("2022-08-01 01:01:01", DateTimeV2Literal::new); + check("2022-08-01 01:01", DateTimeV2Literal::new); + check("2022-08-01 01", DateTimeV2Literal::new); - new DateTimeV2Literal("2022-08-01T01:01:01"); - new DateTimeV2Literal("2022-08-01T01:01"); - new DateTimeV2Literal("2022-08-01T01"); + check("2022-08-01T01:01:01", DateTimeV2Literal::new); + check("2022-08-01T01:01", DateTimeV2Literal::new); + check("2022-08-01T01", DateTimeV2Literal::new); - new DateTimeV2Literal("22-08-01T01:01:01"); - new DateTimeV2Literal("22-08-01T01:01"); - new DateTimeV2Literal("22-08-01T01"); + check("22-08-01T01:01:01", DateTimeV2Literal::new); + check("22-08-01T01:01", DateTimeV2Literal::new); + check("22-08-01T01", DateTimeV2Literal::new); } @Test @@ -112,102 +115,102 @@ class DateTimeLiteralTest { @Test void testTwoDigitYear() { - new DateTimeV2Literal("22-08-01T01"); - new DateTimeV2Literal("22-08-01 01"); - new DateTimeV2Literal("22-08-01T01:01"); - new DateTimeV2Literal("22-08-01 01:01"); - new DateTimeV2Literal("22-08-01T01:01:01"); - new DateTimeV2Literal("22-08-01 01:01:01"); - new DateTimeV2Literal("22-08-01T01"); - new DateTimeV2Literal("22-08-01 01"); - new DateTimeV2Literal("22-08-01T01:01"); - new DateTimeV2Literal("22-08-01 01:01"); - new DateTimeV2Literal("22-08-01T01:01:01"); - new DateTimeV2Literal("22-08-01 01:01:01"); + check("22-08-01T01", DateTimeV2Literal::new); + check("22-08-01 01", DateTimeV2Literal::new); + check("22-08-01T01:01", DateTimeV2Literal::new); + check("22-08-01 01:01", DateTimeV2Literal::new); + check("22-08-01T01:01:01", DateTimeV2Literal::new); + check("22-08-01 01:01:01", DateTimeV2Literal::new); + check("22-08-01T01", DateTimeV2Literal::new); + check("22-08-01 01", DateTimeV2Literal::new); + check("22-08-01T01:01", DateTimeV2Literal::new); + check("22-08-01 01:01", DateTimeV2Literal::new); + check("22-08-01T01:01:01", DateTimeV2Literal::new); + check("22-08-01 01:01:01", DateTimeV2Literal::new); } @Test void testZone() { - new DateTimeV2Literal("2022-08-01 01:01:01UTC"); - new DateTimeV2Literal("2022-08-01 01:01:01UT"); - new DateTimeV2Literal("2022-08-01 01:01:01GMT"); - new DateTimeV2Literal("2022-08-01 01:01:01Z"); - new DateTimeV2Literal("2022-08-01 01:01:01Europe/London"); - new DateTimeV2Literal("2022-08-01 01:01:01America/New_York"); - new DateTimeV2Literal("2022-08-01 01:01:01Z"); - new DateTimeV2Literal("2022-08-01 01:01:01Europe/Berlin"); - new DateTimeV2Literal("2022-08-01 01:01:01Europe/London"); - new DateTimeV2Literal("2022-08-01 00:00:00Asia/Shanghai"); + check("2022-08-01 01:01:01UTC", DateTimeV2Literal::new); + check("2022-08-01 01:01:01UT", DateTimeV2Literal::new); + check("2022-08-01 01:01:01GMT", DateTimeV2Literal::new); + check("2022-08-01 01:01:01Z", DateTimeV2Literal::new); + check("2022-08-01 01:01:01Europe/London", DateTimeV2Literal::new); + check("2022-08-01 01:01:01America/New_York", DateTimeV2Literal::new); + check("2022-08-01 01:01:01Z", DateTimeV2Literal::new); + check("2022-08-01 01:01:01Europe/Berlin", DateTimeV2Literal::new); + check("2022-08-01 01:01:01Europe/London", DateTimeV2Literal::new); + check("2022-08-01 00:00:00Asia/Shanghai", DateTimeV2Literal::new); } @Test void testTwoDigitalYearZone() { - new DateTimeV2Literal("22-08-01 01:01:01UTC"); - new DateTimeV2Literal("22-08-01 01:01:01UT"); - new DateTimeV2Literal("22-08-01 01:01:01GMT"); - new DateTimeV2Literal("22-08-01 01:01:01Z"); - new DateTimeV2Literal("22-08-01 01:01:01Europe/London"); - new DateTimeV2Literal("22-08-01 01:01:01UTC"); - new DateTimeV2Literal("22-08-01 01:01:01America/New_York"); - new DateTimeV2Literal("22-08-01 01:01:01Z"); - new DateTimeV2Literal("22-08-01 01:01:01Europe/Berlin"); - new DateTimeV2Literal("22-08-01 01:01:01Europe/London"); + check("22-08-01 01:01:01UTC", DateTimeV2Literal::new); + check("22-08-01 01:01:01UT", DateTimeV2Literal::new); + check("22-08-01 01:01:01GMT", DateTimeV2Literal::new); + check("22-08-01 01:01:01Z", DateTimeV2Literal::new); + check("22-08-01 01:01:01Europe/London", DateTimeV2Literal::new); + check("22-08-01 01:01:01UTC", DateTimeV2Literal::new); + check("22-08-01 01:01:01America/New_York", DateTimeV2Literal::new); + check("22-08-01 01:01:01Z", DateTimeV2Literal::new); + check("22-08-01 01:01:01Europe/Berlin", DateTimeV2Literal::new); + check("22-08-01 01:01:01Europe/London", DateTimeV2Literal::new); } @Test @Disabled void testTwoDigitalYearZoneOffset() { - new DateTimeV2Literal("22-08-01 01:01:01UTC+01:01:01"); - new DateTimeV2Literal("22-08-01 01:01:01UTC+1:1:1"); + check("22-08-01 01:01:01UTC+01:01:01", DateTimeV2Literal::new); + check("22-08-01 01:01:01UTC+1:1:1", DateTimeV2Literal::new); - new DateTimeV2Literal("22-08-01 01:01:01UTC+01:01"); + check("22-08-01 01:01:01UTC+01:01", DateTimeV2Literal::new); - new DateTimeV2Literal("22-08-01 01:01:01UTC+01"); - new DateTimeV2Literal("22-08-01 01:01:01UTC+1"); + check("22-08-01 01:01:01UTC+01", DateTimeV2Literal::new); + check("22-08-01 01:01:01UTC+1", DateTimeV2Literal::new); } @Test void testOffset() { - new DateTimeV2Literal("2022-05-01 01:02:55+02:30"); - new DateTimeV2Literal("2022-05-01 01:02:55.123-02:30"); - new DateTimeV2Literal("2022-06-01T01:02:55+04:30"); - new DateTimeV2Literal("2022-06-01 01:02:55.123-07:30"); - new DateTimeV2Literal("2022-05-01 01:02:55+02:30"); - - new DateTimeV2Literal("2022-05-01 01:02:55.123-02:30"); - new DateTimeV2Literal("2022-06-01T01:02:55+04:30"); - new DateTimeV2Literal("2022-06-01 01:02:55.123-07:30"); - - new DateTimeV2Literal("20220701010255+07:00"); - new DateTimeV2Literal("20220701010255-05:00"); + check("2022-05-01 01:02:55+02:30", DateTimeV2Literal::new); + check("2022-05-01 01:02:55.123-02:30", DateTimeV2Literal::new); + check("2022-06-01T01:02:55+04:30", DateTimeV2Literal::new); + check("2022-06-01 01:02:55.123-07:30", DateTimeV2Literal::new); + check("2022-05-01 01:02:55+02:30", DateTimeV2Literal::new); + + check("2022-05-01 01:02:55.123-02:30", DateTimeV2Literal::new); + check("2022-06-01T01:02:55+04:30", DateTimeV2Literal::new); + check("2022-06-01 01:02:55.123-07:30", DateTimeV2Literal::new); + + check("20220701010255+07:00", DateTimeV2Literal::new); + check("20220701010255-05:00", DateTimeV2Literal::new); } @Test @Disabled void testDateTimeZone() { - new DateTimeV2Literal("0001-01-01 00:01:01"); - new DateTimeV2Literal("0001-01-01 00:01:01.001"); - new DateTimeV2Literal("0001-01-01 00:01:01.00305"); - - new DateTimeV2Literal("2022-01-01 01:02:55"); - new DateTimeV2Literal("2022-01-01 01:02:55.123"); - new DateTimeV2Literal("2022-02-01 01:02:55Z"); - new DateTimeV2Literal("2022-02-01 01:02:55.123Z"); - new DateTimeV2Literal("2022-03-01 01:02:55UTC+8"); - new DateTimeV2Literal("2022-03-01 01:02:55.123UTC"); - new DateTimeV2Literal("2022-04-01 01:02:55UTC-6"); - new DateTimeV2Literal("2022-04-01T01:02:55UTC-6"); - new DateTimeV2Literal("2022-04-01T01:02:55.123UTC+6"); - - new DateTimeV2Literal("2022-01-01 01:02:55"); - new DateTimeV2Literal("2022-01-01 01:02:55.123"); - new DateTimeV2Literal("2022-02-01 01:02:55Z"); - new DateTimeV2Literal("2022-02-01 01:02:55.123Z"); - new DateTimeV2Literal("2022-03-01 01:02:55UTC+8"); - new DateTimeV2Literal("2022-03-01 01:02:55.123UTC"); - new DateTimeV2Literal("2022-04-01T01:02:55UTC-6"); - - new DateTimeV2Literal("0001-01-01"); + check("0001-01-01 00:01:01", DateTimeV2Literal::new); + check("0001-01-01 00:01:01.001", DateTimeV2Literal::new); + check("0001-01-01 00:01:01.00305", DateTimeV2Literal::new); + + check("2022-01-01 01:02:55", DateTimeV2Literal::new); + check("2022-01-01 01:02:55.123", DateTimeV2Literal::new); + check("2022-02-01 01:02:55Z", DateTimeV2Literal::new); + check("2022-02-01 01:02:55.123Z", DateTimeV2Literal::new); + check("2022-03-01 01:02:55UTC+8", DateTimeV2Literal::new); + check("2022-03-01 01:02:55.123UTC", DateTimeV2Literal::new); + check("2022-04-01 01:02:55UTC-6", DateTimeV2Literal::new); + check("2022-04-01T01:02:55UTC-6", DateTimeV2Literal::new); + check("2022-04-01T01:02:55.123UTC+6", DateTimeV2Literal::new); + + check("2022-01-01 01:02:55", DateTimeV2Literal::new); + check("2022-01-01 01:02:55.123", DateTimeV2Literal::new); + check("2022-02-01 01:02:55Z", DateTimeV2Literal::new); + check("2022-02-01 01:02:55.123Z", DateTimeV2Literal::new); + check("2022-03-01 01:02:55UTC+8", DateTimeV2Literal::new); + check("2022-03-01 01:02:55.123UTC", DateTimeV2Literal::new); + check("2022-04-01T01:02:55UTC-6", DateTimeV2Literal::new); + + check("0001-01-01", DateTimeV2Literal::new); } @Test @@ -221,191 +224,191 @@ class DateTimeLiteralTest { Assertions.assertEquals(0, datetime.second); }; DateTimeV2Literal literal; - literal = new DateTimeV2Literal("2022-01-02 12:00:00UTC+08:00"); + literal = check("2022-01-02 12:00:00UTC+08:00", DateTimeV2Literal::new); assertFunc.accept(literal); - literal = new DateTimeV2Literal("2022-01-02 04:00:00UTC"); + literal = check("2022-01-02 04:00:00UTC", DateTimeV2Literal::new); assertFunc.accept(literal); - literal = new DateTimeV2Literal("2022-01-01 20:00:00UTC-08:00"); + literal = check("2022-01-01 20:00:00UTC-08:00", DateTimeV2Literal::new); assertFunc.accept(literal); - literal = new DateTimeV2Literal("2022-01-02 04:00:00Z"); + literal = check("2022-01-02 04:00:00Z", DateTimeV2Literal::new); assertFunc.accept(literal); } @Test void testIrregularDateTime() { - new DateTimeV2Literal("2016-7-02 01:01:00"); - new DateTimeV2Literal("2016-07-2 01:01:00"); - new DateTimeV2Literal("2016-7-2 01:01:00"); - - new DateTimeV2Literal("2016-07-02 1:01:00"); - new DateTimeV2Literal("2016-07-02 01:1:00"); - new DateTimeV2Literal("2016-07-02 01:01:0"); - new DateTimeV2Literal("2016-07-02 1:1:00"); - new DateTimeV2Literal("2016-07-02 1:01:0"); - new DateTimeV2Literal("2016-07-02 10:1:0"); - new DateTimeV2Literal("2016-07-02 1:1:0"); - - new DateTimeV2Literal("2016-7-2 1:1:0"); - new DateTimeV2Literal("2016-7-02 1:01:0"); - new DateTimeV2Literal("2016-07-2 1:1:0"); - new DateTimeV2Literal("2016-7-02 01:01:0"); - new DateTimeV2Literal("2016-7-2 01:1:0"); + check("2016-7-02 01:01:00", DateTimeV2Literal::new); + check("2016-07-2 01:01:00", DateTimeV2Literal::new); + check("2016-7-2 01:01:00", DateTimeV2Literal::new); + + check("2016-07-02 1:01:00", DateTimeV2Literal::new); + check("2016-07-02 01:1:00", DateTimeV2Literal::new); + check("2016-07-02 01:01:0", DateTimeV2Literal::new); + check("2016-07-02 1:1:00", DateTimeV2Literal::new); + check("2016-07-02 1:01:0", DateTimeV2Literal::new); + check("2016-07-02 10:1:0", DateTimeV2Literal::new); + check("2016-07-02 1:1:0", DateTimeV2Literal::new); + + check("2016-7-2 1:1:0", DateTimeV2Literal::new); + check("2016-7-02 1:01:0", DateTimeV2Literal::new); + check("2016-07-2 1:1:0", DateTimeV2Literal::new); + check("2016-7-02 01:01:0", DateTimeV2Literal::new); + check("2016-7-2 01:1:0", DateTimeV2Literal::new); } @Test void testIrregularDateTimeHour() { - new DateTimeV2Literal("2016-07-02 01"); - new DateTimeV2Literal("2016-07-02 1"); + check("2016-07-02 01", DateTimeV2Literal::new); + check("2016-07-02 1", DateTimeV2Literal::new); - new DateTimeV2Literal("2016-7-02 1"); - new DateTimeV2Literal("2016-7-02 01"); + check("2016-7-02 1", DateTimeV2Literal::new); + check("2016-7-02 01", DateTimeV2Literal::new); - new DateTimeV2Literal("2016-07-2 1"); - new DateTimeV2Literal("2016-07-2 01"); + check("2016-07-2 1", DateTimeV2Literal::new); + check("2016-07-2 01", DateTimeV2Literal::new); - new DateTimeV2Literal("2016-7-2 1"); - new DateTimeV2Literal("2016-7-2 01"); + check("2016-7-2 1", DateTimeV2Literal::new); + check("2016-7-2 01", DateTimeV2Literal::new); } @Test void testIrregularDateTimeHourMinute() { - new DateTimeV2Literal("2016-07-02 01:01"); - new DateTimeV2Literal("2016-07-02 1:01"); - new DateTimeV2Literal("2016-07-02 01:1"); - new DateTimeV2Literal("2016-07-02 1:1"); - - new DateTimeV2Literal("2016-7-02 01:01"); - new DateTimeV2Literal("2016-7-02 1:01"); - new DateTimeV2Literal("2016-7-02 01:1"); - new DateTimeV2Literal("2016-7-02 1:1"); - - new DateTimeV2Literal("2016-07-2 01:01"); - new DateTimeV2Literal("2016-07-2 1:01"); - new DateTimeV2Literal("2016-07-2 01:1"); - new DateTimeV2Literal("2016-07-2 1:1"); - - new DateTimeV2Literal("2016-7-2 01:01"); - new DateTimeV2Literal("2016-7-2 1:01"); - new DateTimeV2Literal("2016-7-2 01:1"); - new DateTimeV2Literal("2016-7-2 1:1"); + check("2016-07-02 01:01", DateTimeV2Literal::new); + check("2016-07-02 1:01", DateTimeV2Literal::new); + check("2016-07-02 01:1", DateTimeV2Literal::new); + check("2016-07-02 1:1", DateTimeV2Literal::new); + + check("2016-7-02 01:01", DateTimeV2Literal::new); + check("2016-7-02 1:01", DateTimeV2Literal::new); + check("2016-7-02 01:1", DateTimeV2Literal::new); + check("2016-7-02 1:1", DateTimeV2Literal::new); + + check("2016-07-2 01:01", DateTimeV2Literal::new); + check("2016-07-2 1:01", DateTimeV2Literal::new); + check("2016-07-2 01:1", DateTimeV2Literal::new); + check("2016-07-2 1:1", DateTimeV2Literal::new); + + check("2016-7-2 01:01", DateTimeV2Literal::new); + check("2016-7-2 1:01", DateTimeV2Literal::new); + check("2016-7-2 01:1", DateTimeV2Literal::new); + check("2016-7-2 1:1", DateTimeV2Literal::new); } @Test void testIrregularDateTimeHourMinuteSecond() { - new DateTimeV2Literal("2016-07-02 01:01:01"); - new DateTimeV2Literal("2016-07-02 1:01:01"); - new DateTimeV2Literal("2016-07-02 01:1:01"); - new DateTimeV2Literal("2016-07-02 1:1:01"); - new DateTimeV2Literal("2016-07-02 01:01:1"); - new DateTimeV2Literal("2016-07-02 1:01:1"); - new DateTimeV2Literal("2016-07-02 01:1:1"); - new DateTimeV2Literal("2016-07-02 1:1:1"); - - new DateTimeV2Literal("2016-7-02 01:01:01"); - new DateTimeV2Literal("2016-7-02 1:01:01"); - new DateTimeV2Literal("2016-7-02 01:1:01"); - new DateTimeV2Literal("2016-7-02 1:1:01"); - new DateTimeV2Literal("2016-7-02 01:01:1"); - new DateTimeV2Literal("2016-7-02 1:01:1"); - new DateTimeV2Literal("2016-7-02 01:1:1"); - new DateTimeV2Literal("2016-7-02 1:1:1"); - - new DateTimeV2Literal("2016-07-2 01:01:01"); - new DateTimeV2Literal("2016-07-2 1:01:01"); - new DateTimeV2Literal("2016-07-2 01:1:01"); - new DateTimeV2Literal("2016-07-2 1:1:01"); - new DateTimeV2Literal("2016-07-2 01:01:1"); - new DateTimeV2Literal("2016-07-2 1:01:1"); - new DateTimeV2Literal("2016-07-2 01:1:1"); - new DateTimeV2Literal("2016-07-2 1:1:1"); - - new DateTimeV2Literal("2016-7-2 01:01:01"); - new DateTimeV2Literal("2016-7-2 1:01:01"); - new DateTimeV2Literal("2016-7-2 01:1:01"); - new DateTimeV2Literal("2016-7-2 1:1:01"); - new DateTimeV2Literal("2016-7-2 01:01:1"); - new DateTimeV2Literal("2016-7-2 1:01:1"); - new DateTimeV2Literal("2016-7-2 01:1:1"); - new DateTimeV2Literal("2016-7-2 1:1:1"); + check("2016-07-02 01:01:01", DateTimeV2Literal::new); + check("2016-07-02 1:01:01", DateTimeV2Literal::new); + check("2016-07-02 01:1:01", DateTimeV2Literal::new); + check("2016-07-02 1:1:01", DateTimeV2Literal::new); + check("2016-07-02 01:01:1", DateTimeV2Literal::new); + check("2016-07-02 1:01:1", DateTimeV2Literal::new); + check("2016-07-02 01:1:1", DateTimeV2Literal::new); + check("2016-07-02 1:1:1", DateTimeV2Literal::new); + + check("2016-7-02 01:01:01", DateTimeV2Literal::new); + check("2016-7-02 1:01:01", DateTimeV2Literal::new); + check("2016-7-02 01:1:01", DateTimeV2Literal::new); + check("2016-7-02 1:1:01", DateTimeV2Literal::new); + check("2016-7-02 01:01:1", DateTimeV2Literal::new); + check("2016-7-02 1:01:1", DateTimeV2Literal::new); + check("2016-7-02 01:1:1", DateTimeV2Literal::new); + check("2016-7-02 1:1:1", DateTimeV2Literal::new); + + check("2016-07-2 01:01:01", DateTimeV2Literal::new); + check("2016-07-2 1:01:01", DateTimeV2Literal::new); + check("2016-07-2 01:1:01", DateTimeV2Literal::new); + check("2016-07-2 1:1:01", DateTimeV2Literal::new); + check("2016-07-2 01:01:1", DateTimeV2Literal::new); + check("2016-07-2 1:01:1", DateTimeV2Literal::new); + check("2016-07-2 01:1:1", DateTimeV2Literal::new); + check("2016-07-2 1:1:1", DateTimeV2Literal::new); + + check("2016-7-2 01:01:01", DateTimeV2Literal::new); + check("2016-7-2 1:01:01", DateTimeV2Literal::new); + check("2016-7-2 01:1:01", DateTimeV2Literal::new); + check("2016-7-2 1:1:01", DateTimeV2Literal::new); + check("2016-7-2 01:01:1", DateTimeV2Literal::new); + check("2016-7-2 1:01:1", DateTimeV2Literal::new); + check("2016-7-2 01:1:1", DateTimeV2Literal::new); + check("2016-7-2 1:1:1", DateTimeV2Literal::new); } @Test void testIrregularDateTimeHourMinuteSecondMicrosecond() { - new DateTimeV2Literal("2016-07-02 01:01:01.1"); - new DateTimeV2Literal("2016-07-02 1:01:01.1"); - new DateTimeV2Literal("2016-07-02 01:1:01.1"); - new DateTimeV2Literal("2016-07-02 1:1:01.1"); - new DateTimeV2Literal("2016-07-02 01:01:1.1"); - new DateTimeV2Literal("2016-07-02 1:01:1.1"); - new DateTimeV2Literal("2016-07-02 01:1:1.1"); - new DateTimeV2Literal("2016-07-02 1:1:1.1"); - - new DateTimeV2Literal("2016-7-02 01:01:01.1"); - new DateTimeV2Literal("2016-7-02 1:01:01.1"); - new DateTimeV2Literal("2016-7-02 01:1:01.1"); - new DateTimeV2Literal("2016-7-02 1:1:01.1"); - new DateTimeV2Literal("2016-7-02 01:01:1.1"); - new DateTimeV2Literal("2016-7-02 1:01:1.1"); - new DateTimeV2Literal("2016-7-02 01:1:1.1"); - new DateTimeV2Literal("2016-7-02 1:1:1.1"); - - new DateTimeV2Literal("2016-07-2 01:01:01.1"); - new DateTimeV2Literal("2016-07-2 1:01:01.1"); - new DateTimeV2Literal("2016-07-2 01:1:01.1"); - new DateTimeV2Literal("2016-07-2 1:1:01.1"); - new DateTimeV2Literal("2016-07-2 01:01:1.1"); - new DateTimeV2Literal("2016-07-2 1:01:1.1"); - new DateTimeV2Literal("2016-07-2 01:1:1.1"); - new DateTimeV2Literal("2016-07-2 1:1:1.1"); - - new DateTimeV2Literal("2016-7-2 01:01:01.1"); - new DateTimeV2Literal("2016-7-2 1:01:01.1"); - new DateTimeV2Literal("2016-7-2 01:1:01.1"); - new DateTimeV2Literal("2016-7-2 1:1:01.1"); - new DateTimeV2Literal("2016-7-2 01:01:1.1"); - new DateTimeV2Literal("2016-7-2 1:01:1.1"); - new DateTimeV2Literal("2016-7-2 01:1:1.1"); - new DateTimeV2Literal("2016-7-2 1:1:1.1"); + check("2016-07-02 01:01:01.1", DateTimeV2Literal::new); + check("2016-07-02 1:01:01.1", DateTimeV2Literal::new); + check("2016-07-02 01:1:01.1", DateTimeV2Literal::new); + check("2016-07-02 1:1:01.1", DateTimeV2Literal::new); + check("2016-07-02 01:01:1.1", DateTimeV2Literal::new); + check("2016-07-02 1:01:1.1", DateTimeV2Literal::new); + check("2016-07-02 01:1:1.1", DateTimeV2Literal::new); + check("2016-07-02 1:1:1.1", DateTimeV2Literal::new); + + check("2016-7-02 01:01:01.1", DateTimeV2Literal::new); + check("2016-7-02 1:01:01.1", DateTimeV2Literal::new); + check("2016-7-02 01:1:01.1", DateTimeV2Literal::new); + check("2016-7-02 1:1:01.1", DateTimeV2Literal::new); + check("2016-7-02 01:01:1.1", DateTimeV2Literal::new); + check("2016-7-02 1:01:1.1", DateTimeV2Literal::new); + check("2016-7-02 01:1:1.1", DateTimeV2Literal::new); + check("2016-7-02 1:1:1.1", DateTimeV2Literal::new); + + check("2016-07-2 01:01:01.1", DateTimeV2Literal::new); + check("2016-07-2 1:01:01.1", DateTimeV2Literal::new); + check("2016-07-2 01:1:01.1", DateTimeV2Literal::new); + check("2016-07-2 1:1:01.1", DateTimeV2Literal::new); + check("2016-07-2 01:01:1.1", DateTimeV2Literal::new); + check("2016-07-2 1:01:1.1", DateTimeV2Literal::new); + check("2016-07-2 01:1:1.1", DateTimeV2Literal::new); + check("2016-07-2 1:1:1.1", DateTimeV2Literal::new); + + check("2016-7-2 01:01:01.1", DateTimeV2Literal::new); + check("2016-7-2 1:01:01.1", DateTimeV2Literal::new); + check("2016-7-2 01:1:01.1", DateTimeV2Literal::new); + check("2016-7-2 1:1:01.1", DateTimeV2Literal::new); + check("2016-7-2 01:01:1.1", DateTimeV2Literal::new); + check("2016-7-2 1:01:1.1", DateTimeV2Literal::new); + check("2016-7-2 01:1:1.1", DateTimeV2Literal::new); + check("2016-7-2 1:1:1.1", DateTimeV2Literal::new); // Testing with microsecond of length 2 - new DateTimeV2Literal("2016-07-02 01:01:01.12"); - new DateTimeV2Literal("2016-7-02 01:01:01.12"); + check("2016-07-02 01:01:01.12", DateTimeV2Literal::new); + check("2016-7-02 01:01:01.12", DateTimeV2Literal::new); // Testing with microsecond of length 3 - new DateTimeV2Literal("2016-07-02 01:01:01.123"); - new DateTimeV2Literal("2016-7-02 01:01:01.123"); + check("2016-07-02 01:01:01.123", DateTimeV2Literal::new); + check("2016-7-02 01:01:01.123", DateTimeV2Literal::new); // Testing with microsecond of length 4 - new DateTimeV2Literal("2016-07-02 01:01:01.1234"); - new DateTimeV2Literal("2016-7-02 01:01:01.1234"); + check("2016-07-02 01:01:01.1234", DateTimeV2Literal::new); + check("2016-7-02 01:01:01.1234", DateTimeV2Literal::new); // Testing with microsecond of length 5 - new DateTimeV2Literal("2016-07-02 01:01:01.12345"); - new DateTimeV2Literal("2016-7-02 01:01:01.12345"); + check("2016-07-02 01:01:01.12345", DateTimeV2Literal::new); + check("2016-7-02 01:01:01.12345", DateTimeV2Literal::new); // Testing with microsecond of length 6 - new DateTimeV2Literal("2016-07-02 01:01:01.123456"); - new DateTimeV2Literal("2016-7-02 01:01:01.123456"); + check("2016-07-02 01:01:01.123456", DateTimeV2Literal::new); + check("2016-7-02 01:01:01.123456", DateTimeV2Literal::new); // Testing with microsecond of length 7 - DateTimeV2Literal literal = new DateTimeV2Literal("2016-07-02 01:01:01.12345678"); + DateTimeV2Literal literal = check("2016-07-02 01:01:01.12345678", DateTimeV2Literal::new); Assertions.assertEquals(123457, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 01:01:01.44444444"); + literal = check("2016-07-02 01:01:01.44444444", DateTimeV2Literal::new); Assertions.assertEquals(444444, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 01:01:01.44444445"); + literal = check("2016-07-02 01:01:01.44444445", DateTimeV2Literal::new); Assertions.assertEquals(444444, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 01:01:01.4444445"); + literal = check("2016-07-02 01:01:01.4444445", DateTimeV2Literal::new); Assertions.assertEquals(444445, literal.microSecond); - literal = new DateTimeV2Literal("2016-07-02 01:01:01.9999995"); + literal = check("2016-07-02 01:01:01.9999995", DateTimeV2Literal::new); Assertions.assertEquals(0, literal.microSecond); Assertions.assertEquals(2, literal.second); - literal = new DateTimeV2Literal("2021-01-01 23:59:59.9999995"); + literal = check("2021-01-01 23:59:59.9999995", DateTimeV2Literal::new); Assertions.assertEquals(0, literal.microSecond); Assertions.assertEquals(0, literal.second); Assertions.assertEquals(0, literal.minute); @@ -415,33 +418,33 @@ class DateTimeLiteralTest { @Test void testDateTimeV2Scale() { Assertions.assertEquals( - new DateTimeV2Literal(DateTimeV2Type.of(3), "2016-07-02 00:00:00.123"), - new DateTimeV2Literal(DateTimeV2Type.of(3), "2016-07-02 00:00:00.123")); + check("2016-07-02 00:00:00.123", s -> new DateTimeV2Literal(DateTimeV2Type.of(3), s)), + check("2016-07-02 00:00:00.123", s -> new DateTimeV2Literal(DateTimeV2Type.of(3), s))); Assertions.assertEquals( - new DateTimeV2Literal(DateTimeV2Type.of(3), "2016-07-02 00:00:00.123456"), - new DateTimeV2Literal(DateTimeV2Type.of(3), "2016-07-02 00:00:00.123")); + check("2016-07-02 00:00:00.123456", s -> new DateTimeV2Literal(DateTimeV2Type.of(3), s)), + check("2016-07-02 00:00:00.123", s -> new DateTimeV2Literal(DateTimeV2Type.of(3), s))); Assertions.assertEquals( - new DateTimeV2Literal(DateTimeV2Type.of(4), "2016-07-02 00:00:00.12345"), - new DateTimeV2Literal(DateTimeV2Type.of(4), "2016-07-02 00:00:00.1235")); + check("2016-07-02 00:00:00.12345", s -> new DateTimeV2Literal(DateTimeV2Type.of(4), s)), + check("2016-07-02 00:00:00.1235", s -> new DateTimeV2Literal(DateTimeV2Type.of(4), s))); Assertions.assertEquals( - new DateTimeV2Literal(DateTimeV2Type.of(0), "2016-07-02 00:00:00.12345"), - new DateTimeV2Literal(DateTimeV2Type.of(0), "2016-07-02 00:00:00")); + check("2016-07-02 00:00:00.12345", s -> new DateTimeV2Literal(DateTimeV2Type.of(0), s)), + check("2016-07-02 00:00:00", s -> new DateTimeV2Literal(DateTimeV2Type.of(0), s))); Assertions.assertEquals( - new DateTimeV2Literal(DateTimeV2Type.of(0), "2016-07-02 00:00:00.5123"), - new DateTimeV2Literal(DateTimeV2Type.of(0), "2016-07-02 00:00:01")); + check("2016-07-02 00:00:00.5123", s -> new DateTimeV2Literal(DateTimeV2Type.of(0), s)), + check("2016-07-02 00:00:01", s -> new DateTimeV2Literal(DateTimeV2Type.of(0), s))); Assertions.assertEquals( - new DateTimeV2Literal(DateTimeV2Type.of(5), "2016-07-02 00:00:00.999999"), - new DateTimeV2Literal(DateTimeV2Type.of(5), "2016-07-02 00:00:01.00000")); + check("2016-07-02 00:00:00.999999", s -> new DateTimeV2Literal(DateTimeV2Type.of(5), s)), + check("2016-07-02 00:00:01.00000", s -> new DateTimeV2Literal(DateTimeV2Type.of(5), s))); // test overflow Assertions.assertEquals( - new DateTimeV2Literal(DateTimeV2Type.of(5), "2016-12-31 23:59:59.999999"), - new DateTimeV2Literal(DateTimeV2Type.of(5), "2017-01-01 00:00:00.00000")); + check("2016-12-31 23:59:59.999999", s -> new DateTimeV2Literal(DateTimeV2Type.of(5), s)), + check("2017-01-01 00:00:00.00000", s -> new DateTimeV2Literal(DateTimeV2Type.of(5), s))); } @Test @@ -485,4 +488,9 @@ class DateTimeLiteralTest { Assertions.assertEquals(l1, l2); Assertions.assertNotEquals(l1, l3); } + + private <L extends DateLiteral> L check(String str, Function<String, L> literalBuilder) { + Assertions.assertTrue(DateTimeChecker.isValidDateTime(str), "Invalid date: " + str); + return literalBuilder.apply(str); + } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteralTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteralTest.java new file mode 100644 index 00000000000..11561846afe --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/FloatLiteralTest.java @@ -0,0 +1,80 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal; + +import org.apache.doris.nereids.trees.expressions.literal.format.FloatChecker; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.function.Function; + +public class FloatLiteralTest { + @Test + public void testChecker() { + assertValid( + "123.45", + "-34.56", + "0", + "0.01", + "10000", + "+1", + "-1", + "+1", + "1.0", + "-1.0", + "-1.0e3", + ".1", + "1.", + "1e3" + ); + + assertInValid( + "e3", + "abc", + "12.34.56", + "1,234.56" + ); + + Assertions.assertThrows( + Throwable.class, + () -> check("e3", s -> new FloatLiteral(new BigDecimal(s).floatValue())) + ); + } + + private void assertValid(String...validString) { + for (String str : validString) { + check(str, s -> new FloatLiteral(new BigDecimal(s).floatValue())); + } + } + + private void assertInValid(String...validString) { + for (String str : validString) { + Assertions.assertThrows( + Throwable.class, + () -> check(str, s -> new FloatLiteral(new BigDecimal(s).floatValue())) + ); + } + } + + private <T extends FractionalLiteral> T check(String str, Function<String, T> literalBuilder) { + Assertions.assertTrue(FloatChecker.isValidFloat(str), "Invalid fractional: " + str); + return literalBuilder.apply(str); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/IntegerLiteralTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/IntegerLiteralTest.java new file mode 100644 index 00000000000..9f23d7fd5ff --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/IntegerLiteralTest.java @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal; + +import org.apache.doris.nereids.trees.expressions.literal.format.IntegerChecker; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.function.Function; + +public class IntegerLiteralTest { + @Test + public void testChecker() { + assertValid( + "1", + "+1", + "-1", + "456" + ); + + assertInValid( + "1.0", + "1e3", + "abc" + ); + } + + private void assertValid(String...validString) { + for (String str : validString) { + check(str, s -> new IntegerLiteral(new BigInteger(s).intValueExact())); + } + } + + private void assertInValid(String...validString) { + for (String str : validString) { + Assertions.assertThrows( + Throwable.class, + () -> check(str, s -> new IntegerLiteral(new BigInteger(s).intValueExact())) + ); + } + } + + private <T extends IntegerLikeLiteral> T check(String str, Function<String, T> literalBuilder) { + Assertions.assertTrue(IntegerChecker.isValidInteger(str), "Invalid integer: " + str); + return literalBuilder.apply(str); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org