This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 35ebef62362 [feature](mtmv) Support to use nondeterministic function
when create async mv (#36111)
35ebef62362 is described below
commit 35ebef62362334fce5d2b901b9f47fd3517abee2
Author: seawinde <[email protected]>
AuthorDate: Fri Jun 21 10:31:58 2024 +0800
[feature](mtmv) Support to use nondeterministic function when create async
mv (#36111)
Support to use current_date() when create async materialized view by
adding
'enable_nondeterministic_function' = 'true' in properties when create
materialized view. `enable_nondeterministic_function` is default false.
Here is a example, it will success
> CREATE MATERIALIZED VIEW mv_name
> BUILD DEFERRED REFRESH AUTO ON MANUAL
> DISTRIBUTED BY RANDOM BUCKETS 2
> PROPERTIES (
> 'replication_num' = '1',
> 'enable_nondeterministic_function' = 'true'
> )
> AS
> SELECT *, unix_timestamp(k3, '%Y-%m-%d %H:%i-%s') from ${tableName}
where current_date() > k3;
Note:
unix_timestamp is nondeterministic when has no params. it is
deterministic when has params which means format column k3 date
another example, it will success
> CREATE MATERIALIZED VIEW mv_name
> BUILD DEFERRED REFRESH AUTO ON MANUAL
> DISTRIBUTED BY RANDOM BUCKETS 2
> PROPERTIES (
> 'replication_num' = '1',
> 'enable_nondeterministic_function' = 'true'
> )
> AS
> SELECT *, unix_timestamp() from ${tableName} where current_date() >
k3;
though unix_timestamp() is nondeterministic, we add
'enable_date_nondeterministic_function' = 'true' in properties
---
.../apache/doris/common/util/PropertyAnalyzer.java | 3 +
.../org/apache/doris/mtmv/MTMVPropertyUtil.java | 7 +-
.../exploration/mv/MaterializedViewUtils.java | 13 ++
.../expressions/functions/ExpressionTrait.java | 22 ++++
.../expressions/functions/Nondeterministic.java | 11 +-
.../functions/scalar/UnixTimestamp.java | 9 +-
.../trees/plans/commands/info/CreateMTMVInfo.java | 25 ++--
.../visitor/NondeterministicFunctionCollector.java | 21 ++--
.../doris/nereids/trees/plans/PlanVisitorTest.java | 99 +++++++++++----
...enable_date_non_deterministic_function_mtmv.out | 11 ++
...ble_date_non_deterministic_function_mtmv.groovy | 136 +++++++++++++++++++++
11 files changed, 307 insertions(+), 50 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
index 69869188c77..6f087d14f4c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
@@ -178,6 +178,9 @@ public class PropertyAnalyzer {
public static final String
PROPERTIES_ENABLE_DUPLICATE_WITHOUT_KEYS_BY_DEFAULT =
"enable_duplicate_without_keys_by_default";
public static final String PROPERTIES_GRACE_PERIOD = "grace_period";
+
+ public static final String PROPERTIES_ENABLE_NONDETERMINISTIC_FUNCTION =
+ "enable_nondeterministic_function";
public static final String PROPERTIES_EXCLUDED_TRIGGER_TABLES =
"excluded_trigger_tables";
public static final String PROPERTIES_REFRESH_PARTITION_NUM =
"refresh_partition_num";
public static final String PROPERTIES_WORKLOAD_GROUP = "workload_group";
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPropertyUtil.java
b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPropertyUtil.java
index a9df9b87d72..12287183886 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPropertyUtil.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVPropertyUtil.java
@@ -30,14 +30,15 @@ import java.util.Optional;
import java.util.Set;
public class MTMVPropertyUtil {
- public static final Set<String> mvPropertyKeys = Sets.newHashSet(
+ public static final Set<String> MV_PROPERTY_KEYS = Sets.newHashSet(
PropertyAnalyzer.PROPERTIES_GRACE_PERIOD,
PropertyAnalyzer.PROPERTIES_EXCLUDED_TRIGGER_TABLES,
PropertyAnalyzer.PROPERTIES_REFRESH_PARTITION_NUM,
PropertyAnalyzer.PROPERTIES_WORKLOAD_GROUP,
PropertyAnalyzer.PROPERTIES_PARTITION_SYNC_LIMIT,
PropertyAnalyzer.PROPERTIES_PARTITION_TIME_UNIT,
- PropertyAnalyzer.PROPERTIES_PARTITION_DATE_FORMAT
+ PropertyAnalyzer.PROPERTIES_PARTITION_DATE_FORMAT,
+ PropertyAnalyzer.PROPERTIES_ENABLE_NONDETERMINISTIC_FUNCTION
);
public static void analyzeProperty(String key, String value) {
@@ -63,6 +64,8 @@ public class MTMVPropertyUtil {
case PropertyAnalyzer.PROPERTIES_PARTITION_SYNC_LIMIT:
analyzePartitionSyncLimit(value);
break;
+ case PropertyAnalyzer.PROPERTIES_ENABLE_NONDETERMINISTIC_FUNCTION:
+ break;
default:
throw new AnalysisException("illegal key:" + key);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
index 7090b745ef8..49e6e7ffc4e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java
@@ -54,6 +54,7 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
import org.apache.doris.nereids.trees.plans.logical.LogicalResultSink;
import org.apache.doris.nereids.trees.plans.logical.LogicalWindow;
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
+import
org.apache.doris.nereids.trees.plans.visitor.NondeterministicFunctionCollector;
import org.apache.doris.nereids.util.ExpressionUtils;
import com.google.common.collect.HashMultimap;
@@ -63,6 +64,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
+import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
@@ -255,6 +257,17 @@ public class MaterializedViewUtils {
rewrittenPlan);
}
+ /**
+ * Extract nondeterministic function form plan, if the function is in
whiteExpressionSet,
+ * the function would be considered as deterministic function and will not
return
+ * in the result expression result
+ */
+ public static List<Expression> extractNondeterministicFunction(Plan plan) {
+ List<Expression> nondeterministicFunctions = new ArrayList<>();
+ plan.accept(NondeterministicFunctionCollector.INSTANCE,
nondeterministicFunctions);
+ return nondeterministicFunctions;
+ }
+
private static final class TableQueryOperatorChecker extends
DefaultPlanVisitor<Boolean, Void> {
public static final TableQueryOperatorChecker INSTANCE = new
TableQueryOperatorChecker();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
index 2d76c78f4cc..2e69a5ecd16 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.types.DataType;
import com.google.common.collect.ImmutableList;
import java.util.List;
+import java.util.Optional;
/**
* ExpressionTrait.
@@ -76,4 +77,25 @@ public interface ExpressionTrait extends
TreeNode<Expression> {
default boolean foldable() {
return true;
}
+
+ /**
+ * Identify the expression is deterministic or not
+ */
+ default boolean isDeterministic() {
+ boolean isDeterministic = true;
+ List<Expression> children = this.children();
+ if (children.isEmpty()) {
+ return isDeterministic;
+ }
+ for (Expression child : children) {
+ Optional<ExpressionTrait> nonDeterministic =
+ child.collectFirst(expressionTreeNode ->
expressionTreeNode instanceof ExpressionTrait
+ && !((ExpressionTrait)
expressionTreeNode).isDeterministic());
+ if (nonDeterministic.isPresent()) {
+ isDeterministic = false;
+ break;
+ }
+ }
+ return isDeterministic;
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Nondeterministic.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Nondeterministic.java
index 56aa5ebb3b9..2d4e2df2fd6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Nondeterministic.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Nondeterministic.java
@@ -19,9 +19,16 @@ package org.apache.doris.nereids.trees.expressions.functions;
/**
* Nondeterministic functions.
- *
+ * <p>
* e.g. 'rand()', 'random()'.
- *
*/
public interface Nondeterministic extends ExpressionTrait {
+
+ /**
+ * Identify the function is deterministic or not, such as UnixTimestamp,
when it's children is not empty
+ * it's deterministic
+ */
+ default boolean isDeterministic() {
+ return false;
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
index 143bc63aade..633e1e7d4f3 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
@@ -20,7 +20,6 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
-import org.apache.doris.nereids.trees.expressions.functions.Nondeterministic;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.DateTimeType;
@@ -40,8 +39,7 @@ import java.util.List;
/**
* ScalarFunction 'unix_timestamp'. This class is generated by
GenerateFunction.
*/
-public class UnixTimestamp extends ScalarFunction
- implements ExplicitlyCastableSignature, Nondeterministic {
+public class UnixTimestamp extends ScalarFunction implements
ExplicitlyCastableSignature {
// we got changes when computeSignature
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
@@ -142,4 +140,9 @@ public class UnixTimestamp extends ScalarFunction
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitUnixTimestamp(this, context);
}
+
+ @Override
+ public boolean isDeterministic() {
+ return !this.children.isEmpty();
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateMTMVInfo.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateMTMVInfo.java
index c4de4dca35d..ab6637964f8 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateMTMVInfo.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateMTMVInfo.java
@@ -54,7 +54,6 @@ import org.apache.doris.nereids.analyzer.UnboundResultSink;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.properties.PhysicalProperties;
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewUtils;
-import org.apache.doris.nereids.trees.TreeNode;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.plans.Plan;
@@ -63,7 +62,6 @@ import
org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.trees.plans.logical.LogicalSink;
import org.apache.doris.nereids.trees.plans.logical.LogicalSubQueryAlias;
-import
org.apache.doris.nereids.trees.plans.visitor.NondeterministicFunctionCollector;
import org.apache.doris.nereids.types.AggStateType;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.util.Utils;
@@ -77,7 +75,6 @@ import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -157,7 +154,7 @@ public class CreateMTMVInfo {
throw new AnalysisException(message);
}
analyzeProperties();
- analyzeQuery(ctx);
+ analyzeQuery(ctx, this.mvProperties);
// analyze column
final boolean finalEnableMergeOnWrite = false;
Set<String> keysSet = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
@@ -189,7 +186,7 @@ public class CreateMTMVInfo {
if
(DynamicPartitionUtil.checkDynamicPartitionPropertiesExist(properties)) {
throw new AnalysisException("Not support dynamic partition
properties on async materialized view");
}
- for (String key : MTMVPropertyUtil.mvPropertyKeys) {
+ for (String key : MTMVPropertyUtil.MV_PROPERTY_KEYS) {
if (properties.containsKey(key)) {
MTMVPropertyUtil.analyzeProperty(key, properties.get(key));
mvProperties.put(key, properties.get(key));
@@ -201,7 +198,7 @@ public class CreateMTMVInfo {
/**
* analyzeQuery
*/
- public void analyzeQuery(ConnectContext ctx) {
+ public void analyzeQuery(ConnectContext ctx, Map<String, String>
mvProperties) {
// create table as select
StatementContext statementContext = ctx.getStatementContext();
NereidsPlanner planner = new NereidsPlanner(statementContext);
@@ -214,7 +211,7 @@ public class CreateMTMVInfo {
// can not contain VIEW or MTMV
analyzeBaseTables(planner.getAnalyzedPlan());
// can not contain Random function
- analyzeExpressions(planner.getAnalyzedPlan());
+ analyzeExpressions(planner.getAnalyzedPlan(), mvProperties);
// can not contain partition or tablets
boolean containTableQueryOperator =
MaterializedViewUtils.containTableQueryOperator(planner.getAnalyzedPlan());
if (containTableQueryOperator) {
@@ -340,11 +337,17 @@ public class CreateMTMVInfo {
}
}
- private void analyzeExpressions(Plan plan) {
- List<TreeNode<Expression>> functionCollectResult = new ArrayList<>();
- plan.accept(NondeterministicFunctionCollector.INSTANCE,
functionCollectResult);
+ private void analyzeExpressions(Plan plan, Map<String, String>
mvProperties) {
+ boolean enableNondeterministicFunction = Boolean.parseBoolean(
+
mvProperties.get(PropertyAnalyzer.PROPERTIES_ENABLE_NONDETERMINISTIC_FUNCTION));
+ if (enableNondeterministicFunction) {
+ return;
+ }
+ List<Expression> functionCollectResult =
MaterializedViewUtils.extractNondeterministicFunction(plan);
if (!CollectionUtils.isEmpty(functionCollectResult)) {
- throw new AnalysisException("can not contain invalid expression");
+ throw new AnalysisException(String.format(
+ "can not contain invalid expression, the expression is %s",
+
functionCollectResult.stream().map(Expression::toString).collect(Collectors.joining(","))));
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/NondeterministicFunctionCollector.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/NondeterministicFunctionCollector.java
index b17be42d383..5b260144575 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/NondeterministicFunctionCollector.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/NondeterministicFunctionCollector.java
@@ -17,31 +17,34 @@
package org.apache.doris.nereids.trees.plans.visitor;
-import org.apache.doris.nereids.trees.TreeNode;
import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.functions.Nondeterministic;
+import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait;
+import org.apache.doris.nereids.trees.expressions.functions.FunctionTrait;
import org.apache.doris.nereids.trees.plans.Plan;
import java.util.List;
+import java.util.Set;
/**
* Collect the nondeterministic expr in plan, these expressions will be put
into context
*/
public class NondeterministicFunctionCollector
- extends DefaultPlanVisitor<Void, List<TreeNode<Expression>>> {
+ extends DefaultPlanVisitor<Void, List<Expression>> {
- public static final NondeterministicFunctionCollector INSTANCE
- = new NondeterministicFunctionCollector();
+ public static final NondeterministicFunctionCollector INSTANCE = new
NondeterministicFunctionCollector();
@Override
- public Void visit(Plan plan, List<TreeNode<Expression>>
collectedExpressions) {
+ public Void visit(Plan plan, List<Expression> collectedExpressions) {
List<? extends Expression> expressions = plan.getExpressions();
if (expressions.isEmpty()) {
return super.visit(plan, collectedExpressions);
}
- expressions.forEach(expression -> {
-
collectedExpressions.addAll(expression.collect(Nondeterministic.class::isInstance));
- });
+ for (Expression expression : expressions) {
+ Set<Expression> nondeterministicFunctions =
+ expression.collect(expr -> !((ExpressionTrait)
expr).isDeterministic()
+ && expr instanceof FunctionTrait);
+ collectedExpressions.addAll(nondeterministicFunctions);
+ }
return super.visit(plan, collectedExpressions);
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanVisitorTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanVisitorTest.java
index 2b3a66b08d7..1afbf6dbbf5 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanVisitorTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanVisitorTest.java
@@ -19,14 +19,15 @@ package org.apache.doris.nereids.trees.plans;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.catalog.TableIf.TableType;
-import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewUtils;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentDate;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentTime;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Now;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Random;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.UnixTimestamp;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Uuid;
import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
-import
org.apache.doris.nereids.trees.plans.visitor.NondeterministicFunctionCollector;
import org.apache.doris.nereids.trees.plans.visitor.TableCollector;
import
org.apache.doris.nereids.trees.plans.visitor.TableCollector.TableCollectorContext;
import org.apache.doris.nereids.util.PlanChecker;
@@ -39,7 +40,6 @@ import mockit.MockUp;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.List;
@@ -61,10 +61,11 @@ public class PlanVisitorTest extends TestWithFeService {
+ " `c1` varchar(20) NULL,\n"
+ " `c2` bigint(20) NULL,\n"
+ " `c3` int(20) not NULL,\n"
+ + " `c4` DATE not NULL,\n"
+ " `k4` bitmap BITMAP_UNION ,\n"
+ " `k5` bitmap BITMAP_UNION \n"
+ ") ENGINE=OLAP\n"
- + "AGGREGATE KEY(`c1`, `c2`, `c3`)\n"
+ + "AGGREGATE KEY(`c1`, `c2`, `c3`, `c4`)\n"
+ "COMMENT 'OLAP'\n"
+ "DISTRIBUTED BY HASH(`c2`) BUCKETS 1\n"
+ "PROPERTIES (\n"
@@ -75,10 +76,11 @@ public class PlanVisitorTest extends TestWithFeService {
+ " `c1` bigint(20) NULL,\n"
+ " `c2` bigint(20) NULL,\n"
+ " `c3` bigint(20) not NULL,\n"
+ + " `c4` DATE not NULL,\n"
+ " `k4` bitmap BITMAP_UNION ,\n"
+ " `k5` bitmap BITMAP_UNION \n"
+ ") ENGINE=OLAP\n"
- + "AGGREGATE KEY(`c1`, `c2`, `c3`)\n"
+ + "AGGREGATE KEY(`c1`, `c2`, `c3`, `c4`)\n"
+ "COMMENT 'OLAP'\n"
+ "DISTRIBUTED BY HASH(`c2`) BUCKETS 1\n"
+ "PROPERTIES (\n"
@@ -89,10 +91,11 @@ public class PlanVisitorTest extends TestWithFeService {
+ " `c1` bigint(20) NULL,\n"
+ " `c2` bigint(20) NULL,\n"
+ " `c3` bigint(20) not NULL,\n"
+ + " `c4` DATE not NULL,\n"
+ " `k4` bitmap BITMAP_UNION ,\n"
+ " `k5` bitmap BITMAP_UNION \n"
+ ") ENGINE=OLAP\n"
- + "AGGREGATE KEY(`c1`, `c2`, `c3`)\n"
+ + "AGGREGATE KEY(`c1`, `c2`, `c3`, `c4`)\n"
+ "COMMENT 'OLAP'\n"
+ "DISTRIBUTED BY HASH(`c2`) BUCKETS 1\n"
+ "PROPERTIES (\n"
@@ -120,11 +123,11 @@ public class PlanVisitorTest extends TestWithFeService {
+ "WHERE table1.c1 IN (SELECT c1 FROM table2)
OR table1.c1 < 10",
nereidsPlanner -> {
PhysicalPlan physicalPlan =
nereidsPlanner.getPhysicalPlan();
- List<TreeNode<Expression>> collectResult = new
ArrayList<>();
// Check nondeterministic collect
-
physicalPlan.accept(NondeterministicFunctionCollector.INSTANCE, collectResult);
- Assertions.assertEquals(1, collectResult.size());
- Assertions.assertTrue(collectResult.get(0)
instanceof Random);
+ List<Expression> nondeterministicFunctionSet =
+
MaterializedViewUtils.extractNondeterministicFunction(physicalPlan);
+ Assertions.assertEquals(1,
nondeterministicFunctionSet.size());
+
Assertions.assertTrue(nondeterministicFunctionSet.get(0) instanceof Random);
// Check get tables
TableCollectorContext collectorContext = new
TableCollector.TableCollectorContext(
Sets.newHashSet(TableType.OLAP), true);
@@ -148,12 +151,12 @@ public class PlanVisitorTest extends TestWithFeService {
+ "WHERE view1.c1 IN (SELECT c1 FROM table2)
OR view1.c1 < 10",
nereidsPlanner -> {
PhysicalPlan physicalPlan =
nereidsPlanner.getPhysicalPlan();
- List<TreeNode<Expression>> collectResult = new
ArrayList<>();
// Check nondeterministic collect
-
physicalPlan.accept(NondeterministicFunctionCollector.INSTANCE, collectResult);
- Assertions.assertEquals(2, collectResult.size());
- Assertions.assertTrue(collectResult.get(0)
instanceof Uuid);
- Assertions.assertTrue(collectResult.get(1)
instanceof Random);
+ List<Expression> nondeterministicFunctionSet =
+
MaterializedViewUtils.extractNondeterministicFunction(physicalPlan);
+ Assertions.assertEquals(2,
nondeterministicFunctionSet.size());
+
Assertions.assertTrue(nondeterministicFunctionSet.get(0) instanceof Uuid);
+
Assertions.assertTrue(nondeterministicFunctionSet.get(1) instanceof Random);
// Check get tables
TableCollectorContext collectorContext = new
TableCollector.TableCollectorContext(
Sets.newHashSet(TableType.OLAP), true);
@@ -186,9 +189,11 @@ public class PlanVisitorTest extends TestWithFeService {
+ "WHERE mv1.c1 IN (SELECT c1 FROM table2) OR
mv1.c1 < 10",
nereidsPlanner -> {
PhysicalPlan physicalPlan =
nereidsPlanner.getPhysicalPlan();
- List<TreeNode<Expression>> collectResult = new
ArrayList<>();
// Check nondeterministic collect
-
physicalPlan.accept(NondeterministicFunctionCollector.INSTANCE, collectResult);
+ List<Expression> nondeterministicFunctionSet =
+
MaterializedViewUtils.extractNondeterministicFunction(physicalPlan);
+ Assertions.assertEquals(1,
nondeterministicFunctionSet.size());
+
Assertions.assertTrue(nondeterministicFunctionSet.get(0) instanceof Uuid);
// Check get tables
TableCollectorContext collectorContext = new
TableCollector.TableCollectorContext(
Sets.newHashSet(TableType.OLAP), true);
@@ -247,14 +252,62 @@ public class PlanVisitorTest extends TestWithFeService {
PlanChecker.from(connectContext)
.checkExplain("SELECT *, now() FROM table1 "
+ "LEFT SEMI JOIN table2 ON table1.c1 =
table2.c1 "
- + "WHERE table1.c1 IN (SELECT c1 FROM table2)
OR CURDATE() < '2023-01-01'",
+ + "WHERE table1.c1 IN (SELECT c1 FROM table2)
OR current_date() < '2023-01-01' and current_time() = '2023-01-10'",
nereidsPlanner -> {
- List<TreeNode<Expression>> collectResult = new
ArrayList<>();
// Check nondeterministic collect
-
nereidsPlanner.getAnalyzedPlan().accept(NondeterministicFunctionCollector.INSTANCE,
collectResult);
- Assertions.assertEquals(2, collectResult.size());
- Assertions.assertTrue(collectResult.get(0)
instanceof Now);
- Assertions.assertTrue(collectResult.get(1)
instanceof CurrentDate);
+ List<Expression> nondeterministicFunctionSet =
+
MaterializedViewUtils.extractNondeterministicFunction(
+ nereidsPlanner.getAnalyzedPlan());
+ Assertions.assertEquals(3,
nondeterministicFunctionSet.size());
+
Assertions.assertTrue(nondeterministicFunctionSet.get(0) instanceof Now);
+
Assertions.assertTrue(nondeterministicFunctionSet.get(1) instanceof
CurrentDate);
+
Assertions.assertTrue(nondeterministicFunctionSet.get(2) instanceof
CurrentTime);
+ });
+ }
+
+ @Test
+ public void testCurrentDateFunction() {
+ PlanChecker.from(connectContext)
+ .checkExplain("SELECT * FROM table1 "
+ + "LEFT SEMI JOIN table2 ON table1.c1 =
table2.c1 "
+ + "WHERE table1.c1 IN (SELECT c1 FROM table2)
OR current_date() < '2023-01-01'",
+ nereidsPlanner -> {
+ // Check nondeterministic collect
+ List<Expression> nondeterministicFunctionSet =
+
MaterializedViewUtils.extractNondeterministicFunction(
+ nereidsPlanner.getAnalyzedPlan());
+ Assertions.assertEquals(1,
nondeterministicFunctionSet.size());
+
Assertions.assertTrue(nondeterministicFunctionSet.get(0) instanceof
CurrentDate);
+ });
+ }
+
+ @Test
+ public void testUnixTimestampWithArgsFunction() {
+ PlanChecker.from(connectContext)
+ .checkExplain("SELECT * FROM table1 "
+ + "LEFT SEMI JOIN table2 ON table1.c1 =
table2.c1 "
+ + "WHERE table1.c1 IN (SELECT c1 FROM table2)
OR unix_timestamp(table1.c4, '%Y-%m-%d %H:%i-%s') < '2023-01-01' and
unix_timestamp(table1.c4) = '2023-01-10'",
+ nereidsPlanner -> {
+ // Check nondeterministic collect
+ List<Expression> nondeterministicFunctionSet =
+
MaterializedViewUtils.extractNondeterministicFunction(
+ nereidsPlanner.getAnalyzedPlan());
+ Assertions.assertEquals(0,
nondeterministicFunctionSet.size());
+ });
+ }
+
+ @Test
+ public void testUnixTimestampWithoutArgsFunction() {
+ PlanChecker.from(connectContext)
+ .checkExplain("SELECT unix_timestamp(), * FROM table1 "
+ + "LEFT SEMI JOIN table2 ON table1.c1 =
table2.c1 ",
+ nereidsPlanner -> {
+ // Check nondeterministic collect
+ List<Expression> nondeterministicFunctionSet =
+
MaterializedViewUtils.extractNondeterministicFunction(
+ nereidsPlanner.getAnalyzedPlan());
+ Assertions.assertEquals(1,
nondeterministicFunctionSet.size());
+
Assertions.assertTrue(nondeterministicFunctionSet.get(0) instanceof
UnixTimestamp);
});
}
}
diff --git
a/regression-test/data/mtmv_p0/test_enable_date_non_deterministic_function_mtmv.out
b/regression-test/data/mtmv_p0/test_enable_date_non_deterministic_function_mtmv.out
new file mode 100644
index 00000000000..e991951431b
--- /dev/null
+++
b/regression-test/data/mtmv_p0/test_enable_date_non_deterministic_function_mtmv.out
@@ -0,0 +1,11 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !with_current_date --
+1 1 2024-05-01 1714492800.000000
+2 2 2024-05-02 1714579200.000000
+3 3 2024-05-03 1714665600.000000
+
+-- !with_unix_timestamp_format --
+1 1 2024-05-01 1714492800.000000
+2 2 2024-05-02 1714579200.000000
+3 3 2024-05-03 1714665600.000000
+
diff --git
a/regression-test/suites/mtmv_p0/test_enable_date_non_deterministic_function_mtmv.groovy
b/regression-test/suites/mtmv_p0/test_enable_date_non_deterministic_function_mtmv.groovy
new file mode 100644
index 00000000000..c085779e707
--- /dev/null
+++
b/regression-test/suites/mtmv_p0/test_enable_date_non_deterministic_function_mtmv.groovy
@@ -0,0 +1,136 @@
+// 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.
+
+import org.junit.Assert;
+
+suite("test_enable_date_non_deterministic_function_mtmv","mtmv") {
+ String suiteName = "test_enable_date_non_deterministic_function_mtmv"
+ String tableName = "${suiteName}_table"
+ String mvName = "${suiteName}_mv"
+ String db = context.config.getDbNameByFile(context.file)
+ sql """drop table if exists `${tableName}`"""
+ sql """drop materialized view if exists ${mvName};"""
+
+ sql """
+ CREATE TABLE ${tableName}
+ (
+ k1 TINYINT,
+ k2 INT not null,
+ k3 DATE NOT NULL
+ )
+ COMMENT "my first table"
+ DISTRIBUTED BY HASH(k2) BUCKETS 2
+ PROPERTIES (
+ "replication_num" = "1"
+ );
+ """
+ sql """
+ insert into ${tableName} values(1,1, '2024-05-01'),(2,2,
'2024-05-02'),(3,3, '2024-05-03');
+ """
+
+ // when not enable date nondeterministic function, create mv should fail
+ // because contains uuid, unix_timestamp, current_date
+ sql """drop materialized view if exists ${mvName};"""
+ try {
+ sql """
+ CREATE MATERIALIZED VIEW ${mvName}
+ BUILD DEFERRED REFRESH AUTO ON MANUAL
+ DISTRIBUTED BY RANDOM BUCKETS 2
+ PROPERTIES ('replication_num' = '1')
+ AS
+ SELECT uuid(), unix_timestamp() FROM ${tableName} where
current_date() > k3;
+ """
+ Assert.fail();
+ } catch (Exception e) {
+ logger.info(e.getMessage())
+ assertTrue(e.getMessage().contains("can not contain invalid
expression"));
+ }
+ sql """drop materialized view if exists ${mvName};"""
+
+
+ // when not enable date nondeterministic function, create mv should fail
+ try {
+ sql """
+ CREATE MATERIALIZED VIEW ${mvName}
+ BUILD DEFERRED REFRESH AUTO ON MANUAL
+ DISTRIBUTED BY RANDOM BUCKETS 2
+ PROPERTIES ('replication_num' = '1')
+ AS
+ SELECT * FROM ${tableName} where current_date() > k3;
+ """
+ Assert.fail();
+ } catch (Exception e) {
+ logger.info(e.getMessage())
+ assertTrue(e.getMessage().contains("can not contain invalid
expression"));
+ }
+ sql """drop materialized view if exists ${mvName};"""
+
+ // when enable date nondeterministic function, create mv with current_date
should success
+ sql """
+ CREATE MATERIALIZED VIEW ${mvName}
+ BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+ DISTRIBUTED BY RANDOM BUCKETS 2
+ PROPERTIES (
+ 'replication_num' = '1',
+ 'enable_nondeterministic_function' = 'true'
+ )
+ AS
+ SELECT *, unix_timestamp(k3, '%Y-%m-%d %H:%i-%s') from ${tableName}
where current_date() > k3;
+ """
+
+ waitingMTMVTaskFinished(getJobName(db, mvName))
+ order_qt_with_current_date """select * from ${mvName}"""
+ sql """drop materialized view if exists ${mvName};"""
+
+
+ sql """drop materialized view if exists ${mvName};"""
+
+ // when disable date nondeterministic function, create mv with param
unix_timestamp should success
+ sql """
+ CREATE MATERIALIZED VIEW ${mvName}
+ BUILD IMMEDIATE REFRESH AUTO ON MANUAL
+ DISTRIBUTED BY RANDOM BUCKETS 2
+ PROPERTIES (
+ 'replication_num' = '1'
+ )
+ AS
+ SELECT *, unix_timestamp(k3, '%Y-%m-%d %H:%i-%s') from ${tableName};
+ """
+
+ waitingMTMVTaskFinished(getJobName(db, mvName))
+ order_qt_with_unix_timestamp_format """select * from ${mvName}"""
+ sql """drop materialized view if exists ${mvName};"""
+
+ // when enable date nondeterministic function, create mv with orther
fuction except current_date() should fail
+ try {
+ sql """
+ CREATE MATERIALIZED VIEW ${mvName}
+ BUILD DEFERRED REFRESH AUTO ON MANUAL
+ DISTRIBUTED BY RANDOM BUCKETS 2
+ PROPERTIES ('replication_num' = '1')
+ AS
+ SELECT * FROM ${tableName} where now() > k3 and current_time()
> k3;
+ """
+ Assert.fail();
+ } catch (Exception e) {
+ logger.info(e.getMessage())
+ assertTrue(e.getMessage().contains("can not contain invalid
expression"));
+ }
+
+ sql """drop table if exists `${tableName}`"""
+ sql """drop materialized view if exists ${mvName};"""
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]