[Fix](Nereids) fold constant result is wrong on functions relative to timezone (#19863)

This commit is contained in:
mch_ucchi
2023-05-31 15:52:40 +08:00
committed by GitHub
parent a1e3f49fb5
commit b53c42636e
10 changed files with 121 additions and 39 deletions

View File

@ -328,10 +328,6 @@ public class FoldConstantRuleOnFE extends AbstractExpressionRewriteRule {
@Override
public Expression visitBoundFunction(BoundFunction boundFunction, ExpressionRewriteContext context) {
boundFunction = rewriteChildren(boundFunction, context);
//functions, like current_date, do not have arg
if (boundFunction.getArguments().isEmpty()) {
return boundFunction;
}
Optional<Expression> checkedExpr = preProcess(boundFunction);
if (checkedExpr.isPresent()) {
return checkedExpr.get();

View File

@ -20,6 +20,7 @@ package org.apache.doris.nereids.trees.expressions;
import org.apache.doris.nereids.analyzer.Unbound;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.AbstractTreeNode;
import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
@ -139,6 +140,10 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
*/
public boolean isConstant() {
if (this instanceof LeafExpression) {
if (this instanceof BoundFunction) {
BoundFunction function = ((BoundFunction) this);
return function instanceof Foldable;
}
return this instanceof Literal;
} else {
return children().stream().allMatch(Expression::isConstant);

View File

@ -137,11 +137,11 @@ public enum ExpressionEvaluator {
ImmutableMultimap.Builder<String, FunctionInvoker> mapBuilder =
new ImmutableMultimap.Builder<String, FunctionInvoker>();
List<Class> classes = ImmutableList.of(
DateTimeAcquire.class,
DateTimeExtractAndTransform.class,
ExecutableFunctions.class,
DateLiteral.class,
DateTimeArithmetic.class,
DateTimeAcquire.class,
NumericArithmetic.class
);
for (Class cls : classes) {

View File

@ -0,0 +1,24 @@
// 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;
/**
* specifically for bound function can be folded to constant.
*/
public interface Foldable {
}

View File

@ -23,9 +23,10 @@ import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
import org.apache.doris.nereids.util.DateUtils;
import java.time.LocalDateTime;
import java.util.TimeZone;
import java.time.ZoneId;
/**
* executable functions:
@ -37,12 +38,12 @@ public class DateTimeAcquire {
*/
@ExecFunction(name = "now", argTypes = {}, returnType = "DATETIME")
public static Expression now() {
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now());
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
@ExecFunction(name = "now", argTypes = {"INT"}, returnType = "DATETIMEV2")
public static Expression now(IntegerLiteral precision) {
return DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(),
return DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()),
precision.getValue());
}
@ -51,12 +52,12 @@ public class DateTimeAcquire {
*/
@ExecFunction(name = "current_timestamp", argTypes = {}, returnType = "DATETIME")
public static Expression currentTimestamp() {
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now());
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
@ExecFunction(name = "current_timestamp", argTypes = {"INT"}, returnType = "DATETIMEV2")
public static Expression currentTimestamp(IntegerLiteral precision) {
return DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(), precision.getValue());
return DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), precision.getValue());
}
/**
@ -64,12 +65,12 @@ public class DateTimeAcquire {
*/
@ExecFunction(name = "localtime", argTypes = {}, returnType = "DATETIME")
public static Expression localTime() {
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now(TimeZone.getDefault().toZoneId()));
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
@ExecFunction(name = "localtimestamp", argTypes = {}, returnType = "DATETIME")
public static Expression localTimestamp() {
return DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(TimeZone.getDefault().toZoneId()));
return DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
/**
@ -77,12 +78,12 @@ public class DateTimeAcquire {
*/
@ExecFunction(name = "curdate", argTypes = {}, returnType = "DATE")
public static Expression curDate() {
return DateLiteral.fromJavaDateType(LocalDateTime.now());
return DateLiteral.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
@ExecFunction(name = "current_date", argTypes = {}, returnType = "DATE")
public static Expression currentDate() {
return DateLiteral.fromJavaDateType(LocalDateTime.now());
return DateLiteral.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
/**
@ -90,11 +91,27 @@ public class DateTimeAcquire {
*/
@ExecFunction(name = "curtime", argTypes = {}, returnType = "DATETIME")
public static Expression curTime() {
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now());
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
@ExecFunction(name = "current_time", argTypes = {}, returnType = "DATETIME")
public static Expression currentTime() {
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now());
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
}
/**
* date transformation function: unix_timestamp
*/
@ExecFunction(name = "unix_timestamp", argTypes = {}, returnType = "INT")
public static Expression unixTimestamp() {
return new IntegerLiteral((int) (System.currentTimeMillis() / 1000L));
}
/**
* date transformation function: utc_timestamp
*/
@ExecFunction(name = "utc_timestamp", argTypes = {}, returnType = "INT")
public static Expression utcTimestamp() {
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now(ZoneId.of("UTC+0")));
}
}

View File

@ -448,20 +448,12 @@ public class DateTimeExtractAndTransform {
.plusSeconds(second.getValue())
.atZone(ZoneId.of("UTC+0"))
.toOffsetDateTime()
.atZoneSameInstant(ZoneId.systemDefault());
.atZoneSameInstant(DateUtils.getTimeZone());
return dateFormat(new DateTimeLiteral(dateTime.getYear(), dateTime.getMonthValue(),
dateTime.getDayOfMonth(), dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond()),
format);
}
/**
* date transformation function: unix_timestamp
*/
@ExecFunction(name = "unix_timestamp", argTypes = {}, returnType = "INT")
public static Expression unixTimestamp() {
return new IntegerLiteral(getTimestamp(LocalDateTime.now()));
}
/**
* date transformation function: unix_timestamp
*/
@ -509,18 +501,9 @@ public class DateTimeExtractAndTransform {
}
return ((int) Duration.between(
specialLowerBound,
dateTime
.atZone(ZoneId.systemDefault())
.toOffsetDateTime().atZoneSameInstant(ZoneId.of("UTC+0"))
.toLocalDateTime()).getSeconds());
}
/**
* date transformation function: utc_timestamp
*/
@ExecFunction(name = "utc_timestamp", argTypes = {}, returnType = "INT")
public static Expression utcTimestamp() {
return DateTimeLiteral.fromJavaDateType(LocalDateTime.now());
dateTime.atZone(DateUtils.getTimeZone())
.toOffsetDateTime().atZoneSameInstant(ZoneId.of("UTC+0"))
.toLocalDateTime()).getSeconds());
}
/**

View File

@ -18,6 +18,7 @@
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Foldable;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import org.apache.doris.nereids.trees.expressions.functions.Nondeterministic;
@ -33,7 +34,7 @@ import java.util.List;
* ScalarFunction 'current_date'. This class is generated by GenerateFunction.
*/
public class CurrentDate extends ScalarFunction
implements LeafExpression, ExplicitlyCastableSignature, Nondeterministic, AlwaysNotNullable {
implements LeafExpression, ExplicitlyCastableSignature, Nondeterministic, AlwaysNotNullable, Foldable {
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateType.INSTANCE).args()

View File

@ -18,6 +18,7 @@
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Foldable;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import org.apache.doris.nereids.trees.expressions.functions.Nondeterministic;
@ -33,7 +34,7 @@ import java.util.List;
* ScalarFunction 'current_time'. This class is generated by GenerateFunction.
*/
public class CurrentTime extends ScalarFunction
implements LeafExpression, ExplicitlyCastableSignature, Nondeterministic, AlwaysNotNullable {
implements LeafExpression, ExplicitlyCastableSignature, Nondeterministic, AlwaysNotNullable, Foldable {
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(TimeType.INSTANCE).args()

View File

@ -18,8 +18,10 @@
package org.apache.doris.nereids.util;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.qe.ConnectContext;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
@ -163,4 +165,11 @@ public class DateUtils {
public static int getOrDefault(final TemporalAccessor accessor, final ChronoField field) {
return accessor.isSupported(field) ? accessor.get(field) : /*default value*/ 0;
}
public static ZoneId getTimeZone() {
if (ConnectContext.get() == null || ConnectContext.get().getSessionVariable() == null) {
return ZoneId.systemDefault();
}
return ZoneId.of(ConnectContext.get().getSessionVariable().getTimeZone());
}
}

View File

@ -0,0 +1,46 @@
// 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.
suite("test_date_acquire") {
sql 'set enable_nereids_planner=true'
sql 'set enable_fallback_to_original_planner=false'
String res = sql 'explain select now(), now(3), curdate(), current_date(), curtime(), current_time(), current_timestamp(), current_timestamp(3)'
res = res.split('VUNION')[1]
assertFalse(res.contains("()") || res.contains("(3)"))
sql "set enable_fold_constant_by_be=true"
test {
sql "select from_unixtime(1553152255), unix_timestamp('2007-11-30 10:30%3A19', '%Y-%m-%d %H:%i%%3A%s')"
result([['2019-03-21 15:10:55', 1196389819]])
}
sql "set time_zone='+00:00'"
test {
sql "select from_unixtime(1553152255), unix_timestamp('2007-11-30 10:30%3A19', '%Y-%m-%d %H:%i%%3A%s')"
result([['2019-03-21 07:10:55', 1196418619]])
}
sql "set time_zone='+04:00'"
test {
sql "select from_unixtime(1553152255), unix_timestamp('2007-11-30 10:30%3A19', '%Y-%m-%d %H:%i%%3A%s')"
result([['2019-03-21 11:10:55', 1196404219]])
}
}