diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index b3e6a3406453d7..e40b83bfe48adc 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -495,6 +495,7 @@ import org.apache.doris.nereids.trees.expressions.Add; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.And; +import org.apache.doris.nereids.trees.expressions.Between; import org.apache.doris.nereids.trees.expressions.BitAnd; import org.apache.doris.nereids.trees.expressions.BitNot; import org.apache.doris.nereids.trees.expressions.BitOr; @@ -4427,16 +4428,15 @@ private Expression withPredicate(Expression valueExpression, PredicateContext ct Expression outExpression; switch (ctx.kind.getType()) { case DorisParser.BETWEEN: - Expression lower = getExpression(ctx.lower); - Expression upper = getExpression(ctx.upper); - if (lower.equals(upper)) { - outExpression = new EqualTo(valueExpression, lower); - } else { - outExpression = new And( - new GreaterThanEqual(valueExpression, getExpression(ctx.lower)), - new LessThanEqual(valueExpression, getExpression(ctx.upper)) - ); - } + // don't compare lower and upper before bind expression, + // for `a between random() and random()` + // the two unbound `'random()` equal, but after bind expression, + // the two `random()` are different. + outExpression = new Between( + valueExpression, + getExpression(ctx.lower), + getExpression(ctx.upper) + ); break; case DorisParser.LIKE: if (ctx.ESCAPE() == null) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java index 62dc65f96dd371..b7f5c98d49ef35 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java @@ -43,6 +43,7 @@ import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.And; import org.apache.doris.nereids.trees.expressions.ArrayItemReference; +import org.apache.doris.nereids.trees.expressions.Between; import org.apache.doris.nereids.trees.expressions.BinaryArithmetic; import org.apache.doris.nereids.trees.expressions.BitNot; import org.apache.doris.nereids.trees.expressions.BoundStar; @@ -53,9 +54,11 @@ import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.ExprId; import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.GreaterThanEqual; import org.apache.doris.nereids.trees.expressions.InPredicate; import org.apache.doris.nereids.trees.expressions.InSubquery; import org.apache.doris.nereids.trees.expressions.IntegralDivide; +import org.apache.doris.nereids.trees.expressions.LessThanEqual; import org.apache.doris.nereids.trees.expressions.Match; import org.apache.doris.nereids.trees.expressions.Not; import org.apache.doris.nereids.trees.expressions.Or; @@ -738,6 +741,21 @@ public Expression visitInPredicate(InPredicate inPredicate, ExpressionRewriteCon return TypeCoercionUtils.processInPredicate(newInPredicate); } + @Override + public Expression visitBetween(Between between, ExpressionRewriteContext context) { + Expression compareExpr = between.getCompareExpr().accept(this, context); + Expression lowerBound = between.getLowerBound().accept(this, context); + Expression upperBound = between.getUpperBound().accept(this, context); + if (lowerBound.equals(upperBound)) { + // rewrite `x between a and a` to `x = a` + return TypeCoercionUtils.processComparisonPredicate(new EqualTo(compareExpr, lowerBound)); + } else { + return new And( + TypeCoercionUtils.processComparisonPredicate(new GreaterThanEqual(compareExpr, lowerBound)), + TypeCoercionUtils.processComparisonPredicate(new LessThanEqual(compareExpr, upperBound))); + } + } + @Override public Expression visitInSubquery(InSubquery inSubquery, ExpressionRewriteContext context) { // analyze subquery diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java new file mode 100644 index 00000000000000..14cc02dadff046 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java @@ -0,0 +1,101 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions; + +import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.BooleanType; +import org.apache.doris.nereids.types.DataType; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * Between predicate expression. + */ +public class Between extends Expression implements TernaryExpression, PropagateNullable { + + private final Expression compareExpr; + private final Expression lowerBound; + private final Expression upperBound; + + /** + * Constructor of ComparisonPredicate. + * + * @param compareExpr compare of expression + * @param lowerBound left child of between predicate + * @param upperBound right child of between predicate + */ + public Between(Expression compareExpr, Expression lowerBound, + Expression upperBound) { + this(ImmutableList.of(compareExpr, lowerBound, upperBound)); + } + + /** + * Constructor of ComparisonPredicate. + * + * @param children 3 children: compareExpr, lowerBound, upperBound + */ + public Between(List children) { + super(children); + this.compareExpr = children.get(0); + this.lowerBound = children.get(1); + this.upperBound = children.get(2); + } + + @Override + public DataType getDataType() throws UnboundException { + return BooleanType.INSTANCE; + } + + @Override + public String computeToSql() { + return compareExpr.toSql() + " BETWEEN " + lowerBound.toSql() + " AND " + upperBound.toSql(); + } + + @Override + public String toString() { + return compareExpr + " BETWEEN " + lowerBound + " AND " + upperBound; + } + + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitBetween(this, context); + } + + public Expression getCompareExpr() { + return compareExpr; + } + + public Expression getLowerBound() { + return lowerBound; + } + + public Expression getUpperBound() { + return upperBound; + } + + @Override + public Between withChildren(List children) { + Preconditions.checkArgument(children.size() == 3); + return new Between(children); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java index 5797a4293421be..625a06ce52b39f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java @@ -37,10 +37,7 @@ * ScalarFunction 'random'. This class is generated by GenerateFunction. * has three signatures: * 1. random(): random a double value between 0 and 1. - * 2. random(seed): random a double value between 0 and 1 with the given seed value, - * in fact, this signature is deterministic and fold-able, - * for example, random(100) always equals 0.9616644308453555. - * but for simple reason, we still treat it as non-deterministic. + * 2. random(seed): random a fix double value sequence between 0 and 1 with the given seed value. * 3. random(a, b): random a big int value between a and b. */ public class Random extends UniqueFunction diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java index 428e4ab6df467d..25578570964809 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java @@ -28,6 +28,7 @@ import org.apache.doris.nereids.trees.expressions.And; import org.apache.doris.nereids.trees.expressions.Any; import org.apache.doris.nereids.trees.expressions.ArrayItemReference; +import org.apache.doris.nereids.trees.expressions.Between; import org.apache.doris.nereids.trees.expressions.BinaryArithmetic; import org.apache.doris.nereids.trees.expressions.BinaryOperator; import org.apache.doris.nereids.trees.expressions.BitAnd; @@ -337,6 +338,10 @@ public R visitStructLiteral(StructLiteral structLiteral, C context) { return visitLiteral(structLiteral, context); } + public R visitBetween(Between between, C context) { + return visit(between, context); + } + public R visitCompoundPredicate(CompoundPredicate compoundPredicate, C context) { return visit(compoundPredicate, context); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java index 71b1b216797668..9c18c6ba32c7db 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java @@ -160,7 +160,7 @@ public static List replaceExpressionByProjections(List 3 or TA <=> null", "TA > 3 or TA <=> null"); assertRewrite("(TA < 1 or TA > 2) or (TA >= 0 and TA <= 3)", "TA IS NOT NULL OR NULL"); assertRewrite("TA between 10 and 20 or TA between 100 and 120 or TA between 15 and 25 or TA between 115 and 125", - "TA between 10 and 25 or TA between 100 and 125"); + "TA >= 10 and TA <= 25 or TA >= 100 and TA <= 125"); assertRewriteNotNull("TA > 3 and TA > null", "TA > 3 and NULL"); assertRewriteNotNull("TA > 3 and TA < null", "TA > 3 and NULL"); assertRewriteNotNull("TA > 3 and TA = null", "TA > 3 and NULL"); @@ -242,6 +242,11 @@ public void testSimplify() { // random is non-foldable, so the two random(1, 10) are distinct, cann't merge range for them. Expression expr = rewrite("TA + random(1, 10) > 10 AND TA + random(1, 10) < 1", Maps.newHashMap()); Assertions.assertEquals("AND[((cast(TA as BIGINT) + random(1, 10)) > 10),((cast(TA as BIGINT) + random(1, 10)) < 1)]", expr.toSql()); + + expr = rewrite("TA + random(1, 10) between 10 and 20", Maps.newHashMap()); + Assertions.assertEquals("AND[((cast(TA as BIGINT) + random(1, 10)) >= 10),((cast(TA as BIGINT) + random(1, 10)) <= 20)]", expr.toSql()); + expr = rewrite("TA + random(1, 10) between 20 and 10", Maps.newHashMap()); + Assertions.assertEquals("AND[(cast(TA as BIGINT) + random(1, 10)) IS NULL,NULL]", expr.toSql()); } @Test diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionEqualsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionEqualsTest.java index ca5793b687c092..c26c0205634832 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionEqualsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionEqualsTest.java @@ -83,6 +83,14 @@ public void testComparisonPredicate() { Assertions.assertEquals(greaterThanEqual1.hashCode(), greaterThanEqual2.hashCode()); } + @Test + public void testBetween() { + Between between1 = new Between(child1, left1, right1); + Between between2 = new Between(child2, left2, right2); + Assertions.assertEquals(between1, between2); + Assertions.assertEquals(between1.hashCode(), between2.hashCode()); + } + @Test public void testNot() { Not not1 = new Not(child1); diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java index cd0e21bb71d7d4..f33bd493824845 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ExpressionParserTest.java @@ -84,9 +84,10 @@ public void testSqlBetweenPredicate() { public void testExprBetweenPredicate() { parseExpression("c BETWEEN a AND b") .assertEquals( - new And( - new GreaterThanEqual(new UnboundSlot("c"), new UnboundSlot("a")), - new LessThanEqual(new UnboundSlot("c"), new UnboundSlot("b")) + new Between( + new UnboundSlot("c"), + new UnboundSlot("a"), + new UnboundSlot("b") ) ); } diff --git a/regression-test/data/nereids_rules_p0/unique_function/between_with_unique_function.out b/regression-test/data/nereids_rules_p0/unique_function/between_with_unique_function.out new file mode 100644 index 00000000000000..9e489a3bfb10c1 --- /dev/null +++ b/regression-test/data/nereids_rules_p0/unique_function/between_with_unique_function.out @@ -0,0 +1,6 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !between_two_num -- +PhysicalResultSink +--filter((cast(id as DOUBLE) <= random()) and (cast(id as DOUBLE) >= random())) +----PhysicalOlapScan[t1] + diff --git a/regression-test/suites/nereids_rules_p0/unique_function/between_with_unique_function.groovy b/regression-test/suites/nereids_rules_p0/unique_function/between_with_unique_function.groovy new file mode 100644 index 00000000000000..b6b633807fd7f1 --- /dev/null +++ b/regression-test/suites/nereids_rules_p0/unique_function/between_with_unique_function.groovy @@ -0,0 +1,22 @@ +// 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('between_with_unique_function') { + sql "set disable_nereids_rules='PRUNE_EMPTY_PARTITION'" + sql "SET ignore_shape_nodes='PhysicalDistribute'" + qt_between_two_num 'explain shape plan select * from t1 where id between random() and random()' +}