[feature](mtmv) materialized view rewrite framework (#27059)
materialized view rewrite framework, support to query rewrite by struct info. The idea is from "Optimizing Queries Using Materialized Views- A Practical, Scalable Solution"
This commit is contained in:
@ -44,6 +44,7 @@ import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.rules.RuleFactory;
|
||||
import org.apache.doris.nereids.rules.RuleSet;
|
||||
import org.apache.doris.nereids.rules.analysis.BindRelation.CustomTableResolver;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.MaterializationContext;
|
||||
import org.apache.doris.nereids.trees.expressions.CTEId;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
@ -112,6 +113,8 @@ public class CascadesContext implements ScheduleContext {
|
||||
private final Optional<CTEId> currentTree;
|
||||
private final Optional<CascadesContext> parent;
|
||||
|
||||
private final List<MaterializationContext> materializationContexts;
|
||||
|
||||
/**
|
||||
* Constructor of OptimizerContext.
|
||||
*
|
||||
@ -133,6 +136,7 @@ public class CascadesContext implements ScheduleContext {
|
||||
this.currentJobContext = new JobContext(this, requireProperties, Double.MAX_VALUE);
|
||||
this.subqueryExprIsAnalyzed = new HashMap<>();
|
||||
this.runtimeFilterContext = new RuntimeFilterContext(getConnectContext().getSessionVariable());
|
||||
this.materializationContexts = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -309,6 +313,14 @@ public class CascadesContext implements ScheduleContext {
|
||||
this.outerScope = Optional.ofNullable(outerScope);
|
||||
}
|
||||
|
||||
public List<MaterializationContext> getMaterializationContexts() {
|
||||
return materializationContexts;
|
||||
}
|
||||
|
||||
public void addMaterializationContext(MaterializationContext materializationContext) {
|
||||
this.materializationContexts.add(materializationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* getAndCacheSessionVariable
|
||||
*/
|
||||
|
||||
@ -259,6 +259,10 @@ public class NereidsPlanner extends Planner {
|
||||
if (statementContext.getConnectContext().getTables() != null) {
|
||||
cascadesContext.setTables(statementContext.getConnectContext().getTables());
|
||||
}
|
||||
if (statementContext.getConnectContext().getSessionVariable().isEnableMaterializedViewRewrite()) {
|
||||
// TODO Pre handle materialized view to materializationContext and
|
||||
// call cascadesContext.addMaterializationContext() to add it
|
||||
}
|
||||
}
|
||||
|
||||
private void analyze() {
|
||||
|
||||
@ -46,6 +46,9 @@ public class OptimizeGroupExpressionJob extends Job {
|
||||
countJobExecutionTimesOfGroupExpressions(groupExpression);
|
||||
List<Rule> implementationRules = getRuleSet().getImplementationRules();
|
||||
List<Rule> explorationRules = getExplorationRules();
|
||||
if (context.getCascadesContext().getConnectContext().getSessionVariable().isEnableMaterializedViewRewrite()) {
|
||||
explorationRules.addAll(getRuleSet().getMaterializedViewRules());
|
||||
}
|
||||
|
||||
for (Rule rule : explorationRules) {
|
||||
if (rule.isInvalid(disableRules, groupExpression)) {
|
||||
|
||||
@ -22,6 +22,7 @@ import org.apache.doris.nereids.cost.Cost;
|
||||
import org.apache.doris.nereids.jobs.joinorder.hypergraph.HyperGraph;
|
||||
import org.apache.doris.nereids.properties.LogicalProperties;
|
||||
import org.apache.doris.nereids.properties.PhysicalProperties;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.StructInfo;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.Literal;
|
||||
import org.apache.doris.nereids.trees.plans.JoinType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
@ -76,6 +77,8 @@ public class Group {
|
||||
|
||||
private int chosenGroupExpressionId = -1;
|
||||
|
||||
private Optional<StructInfo> structInfo = Optional.empty();
|
||||
|
||||
/**
|
||||
* Constructor for Group.
|
||||
*
|
||||
@ -538,4 +541,12 @@ public class Group {
|
||||
|
||||
return TreeStringUtils.treeString(this, toString, getChildren, getExtraPlans, displayExtraPlan);
|
||||
}
|
||||
|
||||
public Optional<StructInfo> getStructInfo() {
|
||||
return structInfo;
|
||||
}
|
||||
|
||||
public void setStructInfo(StructInfo structInfo) {
|
||||
this.structInfo = Optional.ofNullable(structInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ import org.apache.doris.nereids.rules.exploration.join.PushDownProjectThroughInn
|
||||
import org.apache.doris.nereids.rules.exploration.join.PushDownProjectThroughSemiJoin;
|
||||
import org.apache.doris.nereids.rules.exploration.join.SemiJoinSemiJoinTranspose;
|
||||
import org.apache.doris.nereids.rules.exploration.join.SemiJoinSemiJoinTransposeProject;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectJoinRule;
|
||||
import org.apache.doris.nereids.rules.implementation.AggregateStrategies;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalAssertNumRowsToPhysicalAssertNumRows;
|
||||
import org.apache.doris.nereids.rules.implementation.LogicalCTEAnchorToPhysicalCTEAnchor;
|
||||
@ -220,6 +221,10 @@ public class RuleSet {
|
||||
.add(JoinCommute.BUSHY.build())
|
||||
.build();
|
||||
|
||||
public static final List<Rule> MATERIALIZED_VIEW_RULES = planRuleFactories()
|
||||
.add(MaterializedViewProjectJoinRule.INSTANCE)
|
||||
.build();
|
||||
|
||||
public List<Rule> getDPHypReorderRules() {
|
||||
return DPHYP_REORDER_RULES;
|
||||
}
|
||||
@ -240,6 +245,10 @@ public class RuleSet {
|
||||
return IMPLEMENTATION_RULES;
|
||||
}
|
||||
|
||||
public List<Rule> getMaterializedViewRules() {
|
||||
return MATERIALIZED_VIEW_RULES;
|
||||
}
|
||||
|
||||
public static RuleFactories planRuleFactories() {
|
||||
return new RuleFactories();
|
||||
}
|
||||
|
||||
@ -227,6 +227,7 @@ public enum RuleType {
|
||||
MATERIALIZED_INDEX_PROJECT_SCAN(RuleTypeClass.REWRITE),
|
||||
MATERIALIZED_INDEX_PROJECT_FILTER_SCAN(RuleTypeClass.REWRITE),
|
||||
MATERIALIZED_INDEX_FILTER_PROJECT_SCAN(RuleTypeClass.REWRITE),
|
||||
|
||||
OLAP_SCAN_PARTITION_PRUNE(RuleTypeClass.REWRITE),
|
||||
FILE_SCAN_PARTITION_PRUNE(RuleTypeClass.REWRITE),
|
||||
PUSH_CONJUNCTS_INTO_JDBC_SCAN(RuleTypeClass.REWRITE),
|
||||
@ -321,6 +322,22 @@ public enum RuleType {
|
||||
EAGER_SPLIT(RuleTypeClass.EXPLORATION),
|
||||
|
||||
EXPLORATION_SENTINEL(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_PROJECT_JOIN(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_FILTER_JOIN(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_PROJECT_FILTER_JOIN(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_FILTER_PROJECT_JOIN(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_ONLY_JOIN(RuleTypeClass.EXPLORATION),
|
||||
|
||||
MATERIALIZED_VIEW_PROJECT_AGGREGATE(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_FILTER_AGGREGATE(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_PROJECT_FILTER_AGGREGATE(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_FILTER_PROJECT_AGGREGATE(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_ONLY_AGGREGATE(RuleTypeClass.EXPLORATION),
|
||||
|
||||
MATERIALIZED_VIEW_FILTER_SCAN(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_PROJECT_SCAN(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_FILTER_PROJECT_SCAN(RuleTypeClass.EXPLORATION),
|
||||
MATERIALIZED_VIEW_PROJECT_FILTER_SCAN(RuleTypeClass.EXPLORATION),
|
||||
|
||||
// implementation rules
|
||||
LOGICAL_ONE_ROW_RELATION_TO_PHYSICAL_ONE_ROW_RELATION(RuleTypeClass.IMPLEMENTATION),
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
/**
|
||||
* AbstractMaterializedViewAggregateRule
|
||||
* This is responsible for common aggregate rewriting
|
||||
* */
|
||||
public abstract class AbstractMaterializedViewAggregateRule extends AbstractMaterializedViewRule {
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AbstractMaterializedViewJoinRule
|
||||
* This is responsible for common join rewriting
|
||||
*/
|
||||
public abstract class AbstractMaterializedViewJoinRule extends AbstractMaterializedViewRule {
|
||||
|
||||
@Override
|
||||
protected Plan rewriteQueryByView(MatchMode matchMode,
|
||||
StructInfo queryStructInfo,
|
||||
StructInfo viewStructInfo,
|
||||
RelationMapping queryToViewTableMappings,
|
||||
Plan tempRewritedPlan) {
|
||||
|
||||
// Rewrite top projects, represent the query projects by view
|
||||
List<NamedExpression> expressions = rewriteExpression(
|
||||
queryStructInfo.getExpressions(),
|
||||
queryStructInfo,
|
||||
viewStructInfo,
|
||||
queryToViewTableMappings,
|
||||
tempRewritedPlan
|
||||
);
|
||||
// Can not rewrite, bail out
|
||||
if (expressions == null) {
|
||||
return null;
|
||||
}
|
||||
return new LogicalProject<>(expressions, tempRewritedPlan);
|
||||
}
|
||||
|
||||
// Check join is whether valid or not. Support join's input can not contain aggregate
|
||||
// Only support project, filter, join, logical relation node and
|
||||
// join condition should be slot reference equals currently
|
||||
@Override
|
||||
protected boolean checkPattern(StructInfo structInfo) {
|
||||
// TODO Should get struct info from hyper graph and check
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,253 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.catalog.TableIf;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.memo.Group;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.Mapping.ExpressionIndexMapping;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The abstract class for all materialized view rules
|
||||
*/
|
||||
public abstract class AbstractMaterializedViewRule {
|
||||
|
||||
/**
|
||||
* The abstract template method for query rewrite, it contains the main logic and different query
|
||||
* pattern should override the sub logic.
|
||||
*/
|
||||
protected List<Plan> rewrite(Plan queryPlan, CascadesContext cascadesContext) {
|
||||
List<MaterializationContext> materializationContexts = cascadesContext.getMaterializationContexts();
|
||||
List<Plan> rewriteResults = new ArrayList<>();
|
||||
if (materializationContexts.isEmpty()) {
|
||||
return rewriteResults;
|
||||
}
|
||||
StructInfo queryStructInfo = extractStructInfo(queryPlan, cascadesContext);
|
||||
// Check query queryPlan
|
||||
if (!checkPattern(queryStructInfo)) {
|
||||
return rewriteResults;
|
||||
}
|
||||
|
||||
for (MaterializationContext materializationContext : materializationContexts) {
|
||||
Plan mvPlan = materializationContext.getMvPlan();
|
||||
StructInfo viewStructInfo = extractStructInfo(mvPlan, cascadesContext);
|
||||
if (!checkPattern(viewStructInfo)) {
|
||||
continue;
|
||||
}
|
||||
if (!StructInfo.isGraphLogicalEquals(queryStructInfo.getHyperGraph(), viewStructInfo.getHyperGraph())) {
|
||||
continue;
|
||||
}
|
||||
MatchMode matchMode = decideMatchMode(queryStructInfo.getRelations(), viewStructInfo.getRelations());
|
||||
if (MatchMode.NOT_MATCH == matchMode) {
|
||||
continue;
|
||||
}
|
||||
List<RelationMapping> queryToViewTableMappings =
|
||||
RelationMapping.generate(queryStructInfo.getRelations(), viewStructInfo.getRelations());
|
||||
for (RelationMapping queryToViewTableMapping : queryToViewTableMappings) {
|
||||
SplitPredicate compensatePredicates = predicatesCompensate(queryStructInfo, viewStructInfo,
|
||||
queryToViewTableMapping);
|
||||
// Can not compensate, bail out
|
||||
if (compensatePredicates == null || compensatePredicates.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
Plan rewritedPlan;
|
||||
Plan mvScan = materializationContext.getScanPlan();
|
||||
if (compensatePredicates.isAlwaysTrue()) {
|
||||
rewritedPlan = mvScan;
|
||||
} else {
|
||||
// Try to rewrite compensate predicates by using mv scan
|
||||
List<NamedExpression> rewriteCompensatePredicates = rewriteExpression(
|
||||
compensatePredicates.toList(),
|
||||
queryStructInfo,
|
||||
viewStructInfo,
|
||||
queryToViewTableMapping,
|
||||
mvScan);
|
||||
if (rewriteCompensatePredicates.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
rewritedPlan = new LogicalFilter<>(Sets.newHashSet(rewriteCompensatePredicates), mvScan);
|
||||
}
|
||||
// Rewrite query by view
|
||||
rewritedPlan = rewriteQueryByView(matchMode, queryStructInfo, viewStructInfo,
|
||||
queryToViewTableMapping, rewritedPlan);
|
||||
if (rewritedPlan == null) {
|
||||
continue;
|
||||
}
|
||||
rewriteResults.add(rewritedPlan);
|
||||
}
|
||||
}
|
||||
return rewriteResults;
|
||||
}
|
||||
|
||||
/**Rewrite query by view, for aggregate or join rewriting should be different inherit class implementation*/
|
||||
protected Plan rewriteQueryByView(MatchMode matchMode,
|
||||
StructInfo queryStructInfo,
|
||||
StructInfo viewStructInfo,
|
||||
RelationMapping queryToViewTableMappings,
|
||||
Plan tempRewritedPlan) {
|
||||
return tempRewritedPlan;
|
||||
}
|
||||
|
||||
/**Use target output expression to represent the source expression*/
|
||||
protected List<NamedExpression> rewriteExpression(List<? extends Expression> sourceExpressions,
|
||||
StructInfo sourceStructInfo,
|
||||
StructInfo targetStructInfo,
|
||||
RelationMapping sourceToTargetMapping,
|
||||
Plan targetScanNode) {
|
||||
// TODO represent the sourceExpressions by using target scan node
|
||||
// Firstly, rewrite the target plan output expression using query with inverse mapping
|
||||
// then try to use the mv expression to represent the query. if any of source expressions
|
||||
// can not be represented by mv, return null
|
||||
//
|
||||
// example as following:
|
||||
// source target
|
||||
// project(slot 1, 2) project(slot 3, 2, 1)
|
||||
// scan(table) scan(table)
|
||||
//
|
||||
// transform source to:
|
||||
// project(slot 2, 1)
|
||||
// target
|
||||
List<? extends Expression> targetTopExpressions = targetStructInfo.getExpressions();
|
||||
List<? extends Expression> shuttledTargetExpressions = ExpressionUtils.shuttleExpressionWithLineage(
|
||||
targetTopExpressions, targetStructInfo.getOriginalPlan(), Sets.newHashSet(), Sets.newHashSet());
|
||||
SlotMapping sourceToTargetSlotMapping = SlotMapping.generate(sourceToTargetMapping);
|
||||
// mv sql plan expressions transform to query based
|
||||
List<? extends Expression> queryBasedExpressions = ExpressionUtils.permute(shuttledTargetExpressions,
|
||||
sourceToTargetSlotMapping.inverse());
|
||||
// mv sql query based expression and index mapping
|
||||
ExpressionIndexMapping.generate(queryBasedExpressions);
|
||||
// TODO visit source expression and replace the expression with expressionIndexMapping
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compensate mv predicates by query predicates, compensate predicate result is query based.
|
||||
* Such as a > 5 in mv, and a > 10 in query, the compensatory predicate is a > 10.
|
||||
* For another example as following:
|
||||
* predicate a = b in mv, and a = b and c = d in query, the compensatory predicate is c = d
|
||||
*/
|
||||
protected SplitPredicate predicatesCompensate(
|
||||
StructInfo queryStructInfo,
|
||||
StructInfo viewStructInfo,
|
||||
RelationMapping queryToViewTableMapping
|
||||
) {
|
||||
// TODO Equal predicate compensate
|
||||
EquivalenceClass queryEquivalenceClass = queryStructInfo.getEquivalenceClass();
|
||||
EquivalenceClass viewEquivalenceClass = viewStructInfo.getEquivalenceClass();
|
||||
if (queryEquivalenceClass.isEmpty()
|
||||
&& !viewEquivalenceClass.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// TODO range predicates and residual predicates compensate
|
||||
return SplitPredicate.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide the match mode
|
||||
* @see MatchMode
|
||||
*/
|
||||
private MatchMode decideMatchMode(List<CatalogRelation> queryRelations, List<CatalogRelation> viewRelations) {
|
||||
List<TableIf> queryTableRefs = queryRelations
|
||||
.stream()
|
||||
.map(CatalogRelation::getTable)
|
||||
.collect(Collectors.toList());
|
||||
List<TableIf> viewTableRefs = viewRelations
|
||||
.stream()
|
||||
.map(CatalogRelation::getTable)
|
||||
.collect(Collectors.toList());
|
||||
boolean sizeSame = viewTableRefs.size() == queryTableRefs.size();
|
||||
boolean queryPartial = viewTableRefs.containsAll(queryTableRefs);
|
||||
if (!sizeSame && queryPartial) {
|
||||
return MatchMode.QUERY_PARTIAL;
|
||||
}
|
||||
boolean viewPartial = queryTableRefs.containsAll(viewTableRefs);
|
||||
if (!sizeSame && viewPartial) {
|
||||
return MatchMode.VIEW_PARTIAL;
|
||||
}
|
||||
if (sizeSame && queryPartial && viewPartial) {
|
||||
return MatchMode.COMPLETE;
|
||||
}
|
||||
return MatchMode.NOT_MATCH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract struct info from plan, support to get struct info from logical plan or plan in group.
|
||||
*/
|
||||
protected StructInfo extractStructInfo(Plan plan, CascadesContext cascadesContext) {
|
||||
|
||||
if (plan.getGroupExpression().isPresent()
|
||||
&& plan.getGroupExpression().get().getOwnerGroup().getStructInfo().isPresent()) {
|
||||
Group belongGroup = plan.getGroupExpression().get().getOwnerGroup();
|
||||
return belongGroup.getStructInfo().get();
|
||||
} else {
|
||||
// TODO build graph from plan and extract struct from graph and set to group if exist
|
||||
// Should get structInfo from hyper graph and add into current group
|
||||
StructInfo structInfo = StructInfo.of(plan);
|
||||
if (plan.getGroupExpression().isPresent()) {
|
||||
plan.getGroupExpression().get().getOwnerGroup().setStructInfo(structInfo);
|
||||
}
|
||||
return structInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the pattern of query or materializedView is supported or not.
|
||||
*/
|
||||
protected boolean checkPattern(StructInfo structInfo) {
|
||||
if (structInfo.getRelations().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query and mv match node
|
||||
*/
|
||||
protected enum MatchMode {
|
||||
/**
|
||||
* The tables in query are same to the tables in view
|
||||
*/
|
||||
COMPLETE,
|
||||
/**
|
||||
* The tables in query contains all the tables in view
|
||||
*/
|
||||
VIEW_PARTIAL,
|
||||
/**
|
||||
* The tables in view contains all the tables in query
|
||||
*/
|
||||
QUERY_PARTIAL,
|
||||
/**
|
||||
* Except for COMPLETE and VIEW_PARTIAL and QUERY_PARTIAL
|
||||
*/
|
||||
NOT_MATCH
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.trees.expressions.SlotReference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* EquivalenceClass, this is used for equality propagation when predicate compensation
|
||||
*/
|
||||
public class EquivalenceClass {
|
||||
|
||||
private final Map<SlotReference, Set<SlotReference>> equivalenceSlotMap = new LinkedHashMap<>();
|
||||
|
||||
public EquivalenceClass() {
|
||||
}
|
||||
|
||||
/**
|
||||
* EquivalenceClass
|
||||
*/
|
||||
public void addEquivalenceClass(SlotReference leftSlot, SlotReference rightSlot) {
|
||||
|
||||
Set<SlotReference> leftSlotSet = equivalenceSlotMap.get(leftSlot);
|
||||
Set<SlotReference> rightSlotSet = equivalenceSlotMap.get(rightSlot);
|
||||
if (leftSlotSet != null && rightSlotSet != null) {
|
||||
// Both present, we need to merge
|
||||
if (leftSlotSet.size() < rightSlotSet.size()) {
|
||||
// We swap them to merge
|
||||
Set<SlotReference> tmp = rightSlotSet;
|
||||
rightSlotSet = leftSlotSet;
|
||||
leftSlotSet = tmp;
|
||||
}
|
||||
for (SlotReference newRef : rightSlotSet) {
|
||||
leftSlotSet.add(newRef);
|
||||
equivalenceSlotMap.put(newRef, leftSlotSet);
|
||||
}
|
||||
} else if (leftSlotSet != null) {
|
||||
// leftSlotSet present, we need to merge into it
|
||||
leftSlotSet.add(rightSlot);
|
||||
equivalenceSlotMap.put(rightSlot, leftSlotSet);
|
||||
} else if (rightSlotSet != null) {
|
||||
// rightSlotSet present, we need to merge into it
|
||||
rightSlotSet.add(leftSlot);
|
||||
equivalenceSlotMap.put(leftSlot, rightSlotSet);
|
||||
} else {
|
||||
// None are present, add to same equivalence class
|
||||
Set<SlotReference> equivalenceClass = new LinkedHashSet<>();
|
||||
equivalenceClass.add(leftSlot);
|
||||
equivalenceClass.add(rightSlot);
|
||||
equivalenceSlotMap.put(leftSlot, equivalenceClass);
|
||||
equivalenceSlotMap.put(rightSlot, equivalenceClass);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<SlotReference, Set<SlotReference>> getEquivalenceSlotMap() {
|
||||
return equivalenceSlotMap;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return equivalenceSlotMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* EquivalenceClass
|
||||
*/
|
||||
public List<Set<SlotReference>> getEquivalenceValues() {
|
||||
List<Set<SlotReference>> values = new ArrayList<>();
|
||||
equivalenceSlotMap.values().forEach(each -> values.add(each));
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.trees.expressions.ExprId;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.plans.RelationId;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Mapping slot from query to view or inversely,
|
||||
* it can also represent the mapping from slot to it's index
|
||||
*/
|
||||
public abstract class Mapping {
|
||||
|
||||
/**
|
||||
* The relation for mapping
|
||||
*/
|
||||
public static final class MappedRelation {
|
||||
public final RelationId relationId;
|
||||
public final CatalogRelation belongedRelation;
|
||||
|
||||
public MappedRelation(RelationId relationId, CatalogRelation belongedRelation) {
|
||||
this.relationId = relationId;
|
||||
this.belongedRelation = belongedRelation;
|
||||
}
|
||||
|
||||
public MappedRelation of(RelationId relationId, CatalogRelation belongedRelation) {
|
||||
return new MappedRelation(relationId, belongedRelation);
|
||||
}
|
||||
|
||||
public RelationId getRelationId() {
|
||||
return relationId;
|
||||
}
|
||||
|
||||
public CatalogRelation getBelongedRelation() {
|
||||
return belongedRelation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MappedRelation that = (MappedRelation) o;
|
||||
return Objects.equals(relationId, that.relationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(relationId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The slot for mapping
|
||||
*/
|
||||
public static final class MappedSlot {
|
||||
|
||||
public final ExprId exprId;
|
||||
public final CatalogRelation belongedRelation;
|
||||
|
||||
public MappedSlot(ExprId exprId, CatalogRelation belongedRelation) {
|
||||
this.exprId = exprId;
|
||||
this.belongedRelation = belongedRelation;
|
||||
}
|
||||
|
||||
public MappedSlot of(ExprId exprId, CatalogRelation belongedRelation) {
|
||||
return new MappedSlot(exprId, belongedRelation);
|
||||
}
|
||||
|
||||
public ExprId getExprId() {
|
||||
return exprId;
|
||||
}
|
||||
|
||||
public CatalogRelation getBelongedRelation() {
|
||||
return belongedRelation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MappedSlot that = (MappedSlot) o;
|
||||
return Objects.equals(exprId, that.exprId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(exprId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expression and it's index mapping
|
||||
*/
|
||||
public static class ExpressionIndexMapping extends Mapping {
|
||||
private final Multimap<Expression, Integer> expressionIndexMapping;
|
||||
|
||||
public ExpressionIndexMapping(Multimap<Expression, Integer> expressionIndexMapping) {
|
||||
this.expressionIndexMapping = expressionIndexMapping;
|
||||
}
|
||||
|
||||
public Multimap<Expression, Integer> getExpressionIndexMapping() {
|
||||
return expressionIndexMapping;
|
||||
}
|
||||
|
||||
public static ExpressionIndexMapping generate(List<? extends Expression> expressions) {
|
||||
Multimap<Expression, Integer> expressionIndexMapping = ArrayListMultimap.create();
|
||||
for (int i = 0; i < expressions.size(); i++) {
|
||||
expressionIndexMapping.put(expressions.get(i), i);
|
||||
}
|
||||
return new ExpressionIndexMapping(expressionIndexMapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.catalog.Table;
|
||||
import org.apache.doris.catalog.View;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.memo.GroupId;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Maintain the context for query rewrite by materialized view
|
||||
*/
|
||||
public class MaterializationContext {
|
||||
|
||||
// TODO add MaterializedView class
|
||||
private final Plan mvPlan;
|
||||
private final CascadesContext context;
|
||||
private final List<Table> baseTables;
|
||||
private final List<View> baseViews;
|
||||
// Group ids that are rewritten by this mv to reduce rewrite times
|
||||
private final Set<GroupId> matchedGroups = new HashSet<>();
|
||||
private final Plan scanPlan;
|
||||
|
||||
public MaterializationContext(Plan mvPlan, CascadesContext context,
|
||||
List<Table> baseTables, List<View> baseViews, Plan scanPlan) {
|
||||
this.mvPlan = mvPlan;
|
||||
this.context = context;
|
||||
this.baseTables = baseTables;
|
||||
this.baseViews = baseViews;
|
||||
this.scanPlan = scanPlan;
|
||||
}
|
||||
|
||||
public Set<GroupId> getMatchedGroups() {
|
||||
return matchedGroups;
|
||||
}
|
||||
|
||||
public void addMatchedGroup(GroupId groupId) {
|
||||
matchedGroups.add(groupId);
|
||||
}
|
||||
|
||||
public Plan getMvPlan() {
|
||||
return mvPlan;
|
||||
}
|
||||
|
||||
public Plan getScanPlan() {
|
||||
return scanPlan;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.rewrite.RewriteRuleFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is responsible for aggregate rewriting according to different pattern
|
||||
* */
|
||||
public class MaterializedViewAggregateRule extends AbstractMaterializedViewAggregateRule implements RewriteRuleFactory {
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RulePromise;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.rules.rewrite.RewriteRuleFactory;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is responsible for join rewriting according to different pattern
|
||||
* */
|
||||
public class MaterializedViewProjectJoinRule extends AbstractMaterializedViewJoinRule implements RewriteRuleFactory {
|
||||
|
||||
public static final MaterializedViewProjectJoinRule INSTANCE = new MaterializedViewProjectJoinRule();
|
||||
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return ImmutableList.of(
|
||||
logicalProject(logicalJoin(any(), any())).thenApplyMulti(ctx -> {
|
||||
LogicalProject<LogicalJoin<Plan, Plan>> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_ONLY_JOIN, RulePromise.EXPLORE));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.rewrite.RewriteRuleFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is responsible for single table rewriting according to different pattern
|
||||
* */
|
||||
public class MaterializedViewScanRule extends AbstractMaterializedViewRule implements RewriteRuleFactory {
|
||||
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitors.PredicatesSpliter;
|
||||
import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This record the predicates which can be pulled up or some other type predicates
|
||||
* */
|
||||
public class Predicates {
|
||||
|
||||
// Predicates that can be pulled up
|
||||
private final Set<Expression> pulledUpPredicates;
|
||||
|
||||
public Predicates(Set<Expression> pulledUpPredicates) {
|
||||
this.pulledUpPredicates = pulledUpPredicates;
|
||||
}
|
||||
|
||||
public static Predicates of(Set<Expression> pulledUpPredicates) {
|
||||
return new Predicates(pulledUpPredicates);
|
||||
}
|
||||
|
||||
public Set<Expression> getPulledUpPredicates() {
|
||||
return pulledUpPredicates;
|
||||
}
|
||||
|
||||
public Expression composedExpression() {
|
||||
return ExpressionUtils.and(pulledUpPredicates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the expression to equal, range and residual predicate.
|
||||
* */
|
||||
public static SplitPredicate splitPredicates(Expression expression) {
|
||||
PredicatesSpliter predicatesSplit = new PredicatesSpliter(expression);
|
||||
expression.accept(predicatesSplit, null);
|
||||
return predicatesSplit.getSplitPredicate();
|
||||
}
|
||||
|
||||
/**
|
||||
* The split different representation for predicate expression, such as equal, range and residual predicate.
|
||||
* */
|
||||
public static final class SplitPredicate {
|
||||
private final Expression equalPredicates;
|
||||
private final Expression rangePredicates;
|
||||
private final Expression residualPredicates;
|
||||
|
||||
public SplitPredicate(Expression equalPredicates, Expression rangePredicates, Expression residualPredicates) {
|
||||
this.equalPredicates = equalPredicates;
|
||||
this.rangePredicates = rangePredicates;
|
||||
this.residualPredicates = residualPredicates;
|
||||
}
|
||||
|
||||
public Expression getEqualPredicates() {
|
||||
return equalPredicates;
|
||||
}
|
||||
|
||||
public Expression getRangePredicates() {
|
||||
return rangePredicates;
|
||||
}
|
||||
|
||||
public Expression getResidualPredicates() {
|
||||
return residualPredicates;
|
||||
}
|
||||
|
||||
public static SplitPredicate empty() {
|
||||
return new SplitPredicate(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* SplitPredicate construct
|
||||
* */
|
||||
public static SplitPredicate of(Expression equalPredicates,
|
||||
Expression rangePredicates,
|
||||
Expression residualPredicates) {
|
||||
return new SplitPredicate(equalPredicates, rangePredicates, residualPredicates);
|
||||
}
|
||||
|
||||
/**
|
||||
* isEmpty
|
||||
* */
|
||||
public boolean isEmpty() {
|
||||
return equalPredicates == null
|
||||
&& rangePredicates == null
|
||||
&& residualPredicates == null;
|
||||
}
|
||||
|
||||
public Expression composedExpression() {
|
||||
return ExpressionUtils.and(equalPredicates, rangePredicates, residualPredicates);
|
||||
}
|
||||
|
||||
public List<Expression> toList() {
|
||||
return ImmutableList.of(equalPredicates, rangePredicates, residualPredicates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the predicates in SplitPredicate is whether all true or not
|
||||
*/
|
||||
public boolean isAlwaysTrue() {
|
||||
return equalPredicates instanceof BooleanLiteral
|
||||
&& rangePredicates instanceof BooleanLiteral
|
||||
&& residualPredicates instanceof BooleanLiteral
|
||||
&& ((BooleanLiteral) equalPredicates).getValue()
|
||||
&& ((BooleanLiteral) rangePredicates).getValue()
|
||||
&& ((BooleanLiteral) residualPredicates).getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.catalog.TableIf;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Relation mapping
|
||||
* such as query pattern is a1 left join a2 left join b
|
||||
* view pattern is a1 left join a2 left join b. the mapping will be
|
||||
* [{a1:a1, a2:a2, b:b}, {a1:a2, a2:a1, b:b}]
|
||||
*/
|
||||
public class RelationMapping extends Mapping {
|
||||
|
||||
private final BiMap<MappedRelation, MappedRelation> mappedRelationMap;
|
||||
|
||||
public RelationMapping(BiMap<MappedRelation, MappedRelation> mappedRelationMap) {
|
||||
this.mappedRelationMap = mappedRelationMap;
|
||||
}
|
||||
|
||||
public BiMap<MappedRelation, MappedRelation> getMappedRelationMap() {
|
||||
return mappedRelationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate mapping according to source and target relation
|
||||
*/
|
||||
public static List<RelationMapping> generate(List<CatalogRelation> source, List<CatalogRelation> target) {
|
||||
Multimap<TableIf, CatalogRelation> queryTableRelationIdMap = ArrayListMultimap.create();
|
||||
for (CatalogRelation relation : source) {
|
||||
queryTableRelationIdMap.put(relation.getTable(), relation);
|
||||
}
|
||||
Multimap<TableIf, CatalogRelation> viewTableRelationIdMap = ArrayListMultimap.create();
|
||||
for (CatalogRelation relation : target) {
|
||||
viewTableRelationIdMap.put(relation.getTable(), relation);
|
||||
}
|
||||
// todo generate relation map
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
@ -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.rules.exploration.mv;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
|
||||
/**
|
||||
* SlotMapping, this is open generated from relationMapping
|
||||
*/
|
||||
public class SlotMapping extends Mapping {
|
||||
|
||||
private final BiMap<MappedSlot, MappedSlot> relationSlotMap;
|
||||
|
||||
public SlotMapping(BiMap<MappedSlot, MappedSlot> relationSlotMap) {
|
||||
this.relationSlotMap = relationSlotMap;
|
||||
}
|
||||
|
||||
public BiMap<MappedSlot, MappedSlot> getRelationSlotMap() {
|
||||
return relationSlotMap;
|
||||
}
|
||||
|
||||
public SlotMapping inverse() {
|
||||
return SlotMapping.of(relationSlotMap.inverse());
|
||||
}
|
||||
|
||||
public static SlotMapping of(BiMap<MappedSlot, MappedSlot> relationSlotMap) {
|
||||
return new SlotMapping(relationSlotMap);
|
||||
}
|
||||
|
||||
public static SlotMapping generate(RelationMapping relationMapping) {
|
||||
// TODO implement
|
||||
return SlotMapping.of(null);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.jobs.joinorder.hypergraph.HyperGraph;
|
||||
import org.apache.doris.nereids.memo.Group;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
|
||||
import org.apache.doris.nereids.trees.expressions.EqualTo;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.SlotReference;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* StructInfo
|
||||
*/
|
||||
public class StructInfo {
|
||||
private final List<CatalogRelation> relations;
|
||||
private final Predicates predicates;
|
||||
// Used by predicate compensation
|
||||
private final EquivalenceClass equivalenceClass;
|
||||
private final Plan originalPlan;
|
||||
private final HyperGraph hyperGraph;
|
||||
|
||||
private StructInfo(List<CatalogRelation> relations,
|
||||
Predicates predicates,
|
||||
Plan originalPlan,
|
||||
HyperGraph hyperGraph) {
|
||||
this.relations = relations;
|
||||
this.predicates = predicates;
|
||||
this.originalPlan = originalPlan;
|
||||
this.hyperGraph = hyperGraph;
|
||||
// construct equivalenceClass according to equals predicates
|
||||
this.equivalenceClass = new EquivalenceClass();
|
||||
SplitPredicate splitPredicate = Predicates.splitPredicates(predicates.composedExpression());
|
||||
for (Expression expression : ExpressionUtils.extractConjunction(splitPredicate.getEqualPredicates())) {
|
||||
EqualTo equalTo = (EqualTo) expression;
|
||||
equivalenceClass.addEquivalenceClass(
|
||||
(SlotReference) equalTo.getArguments().get(0),
|
||||
(SlotReference) equalTo.getArguments().get(1));
|
||||
}
|
||||
}
|
||||
|
||||
public static StructInfo of(Plan originalPlan) {
|
||||
// TODO build graph from original plan and get relations and predicates from graph
|
||||
return new StructInfo(null, null, originalPlan, null);
|
||||
}
|
||||
|
||||
public static StructInfo of(Group group) {
|
||||
// TODO build graph from original plan and get relations and predicates from graph
|
||||
return new StructInfo(null, null, group.getLogicalExpression().getPlan(), null);
|
||||
}
|
||||
|
||||
public List<CatalogRelation> getRelations() {
|
||||
return relations;
|
||||
}
|
||||
|
||||
public Predicates getPredicates() {
|
||||
return predicates;
|
||||
}
|
||||
|
||||
public EquivalenceClass getEquivalenceClass() {
|
||||
return equivalenceClass;
|
||||
}
|
||||
|
||||
public Plan getOriginalPlan() {
|
||||
return originalPlan;
|
||||
}
|
||||
|
||||
public HyperGraph getHyperGraph() {
|
||||
return hyperGraph;
|
||||
}
|
||||
|
||||
public List<? extends Expression> getExpressions() {
|
||||
return originalPlan instanceof LogicalProject
|
||||
? ((LogicalProject<Plan>) originalPlan).getProjects() : originalPlan.getOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Judge the source graph logical is whether the same as target
|
||||
* For inner join should judge only the join tables,
|
||||
* for other join type should also judge the join direction, it's input filter that can not be pulled up etc.
|
||||
* */
|
||||
public static boolean isGraphLogicalEquals(HyperGraph source, HyperGraph target) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -17,9 +17,16 @@
|
||||
|
||||
package org.apache.doris.nereids.trees.expressions.visitor;
|
||||
|
||||
import org.apache.doris.nereids.rules.exploration.mv.Predicates;
|
||||
import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
|
||||
import org.apache.doris.nereids.trees.expressions.EqualTo;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.WindowExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
|
||||
import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is the factory for all ExpressionVisitor instance.
|
||||
@ -54,4 +61,46 @@ public class ExpressionVisitors {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the expression to equal, range and residual predicate.
|
||||
* Should instance when used.
|
||||
*/
|
||||
public static class PredicatesSpliter extends DefaultExpressionVisitor<Void, Void> {
|
||||
|
||||
private List<Expression> equalPredicates = new ArrayList<>();
|
||||
private List<Expression> rangePredicates = new ArrayList<>();
|
||||
private List<Expression> residualPredicates = new ArrayList<>();
|
||||
private final Expression target;
|
||||
|
||||
public PredicatesSpliter(Expression target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitComparisonPredicate(ComparisonPredicate comparisonPredicate, Void context) {
|
||||
// TODO Smallest implement, complete later
|
||||
if (comparisonPredicate instanceof EqualTo) {
|
||||
Expression leftArgument = comparisonPredicate.getArgument(0);
|
||||
Expression rightArgument = comparisonPredicate.getArgument(1);
|
||||
if (leftArgument.isSlot() && rightArgument.isSlot()) {
|
||||
equalPredicates.add(comparisonPredicate);
|
||||
} else {
|
||||
rangePredicates.add(comparisonPredicate);
|
||||
}
|
||||
}
|
||||
return super.visit(comparisonPredicate, context);
|
||||
}
|
||||
|
||||
public Expression getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public Predicates.SplitPredicate getSplitPredicate() {
|
||||
return Predicates.SplitPredicate.of(
|
||||
equalPredicates.isEmpty() ? null : ExpressionUtils.and(equalPredicates),
|
||||
rangePredicates.isEmpty() ? null : ExpressionUtils.and(rangePredicates),
|
||||
residualPredicates.isEmpty() ? null : ExpressionUtils.and(residualPredicates));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,9 @@
|
||||
|
||||
package org.apache.doris.nereids.util;
|
||||
|
||||
import org.apache.doris.catalog.TableIf.TableType;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.SlotMapping;
|
||||
import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
|
||||
import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule;
|
||||
import org.apache.doris.nereids.trees.TreeNode;
|
||||
@ -39,6 +41,7 @@ import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.Literal;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Predicate;
|
||||
@ -196,6 +199,31 @@ public class ExpressionUtils {
|
||||
.orElse(BooleanLiteral.of(type == And.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the slot in expression with the lineage identifier from specified
|
||||
* baseTable sets or target table types.
|
||||
* <p>
|
||||
* For example as following:
|
||||
* select a + 10 as a1, d from (
|
||||
* select b - 5 as a, d from table
|
||||
* );
|
||||
* after shuttle a1, d in select will be b - 5 + 10, d
|
||||
*/
|
||||
public static List<? extends Expression> shuttleExpressionWithLineage(List<? extends Expression> expression,
|
||||
Plan plan,
|
||||
Set<TableType> targetTypes,
|
||||
Set<String> tableIdentifiers) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the slot in expressions according to the slotMapping
|
||||
* if any slot cannot be mapped then return null
|
||||
*/
|
||||
public static List<? extends Expression> permute(List<? extends Expression> expressions, SlotMapping slotMapping) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the minimum slot from input parameter.
|
||||
*/
|
||||
|
||||
@ -451,6 +451,12 @@ public class SessionVariable implements Serializable, Writable {
|
||||
public static final String TABLE_STATS_HEALTH_THRESHOLD
|
||||
= "table_stats_health_threshold";
|
||||
|
||||
public static final String ENABLE_MATERIALIZED_VIEW_REWRITE
|
||||
= "enable_materialized_view_rewrite";
|
||||
|
||||
public static final String MATERIALIZED_VIEW_REWRITE_ENABLE_CONTAIN_FOREIGN_TABLE
|
||||
= "materialized_view_rewrite_enable_contain_foreign_table";
|
||||
|
||||
public static final List<String> DEBUG_VARIABLES = ImmutableList.of(
|
||||
SKIP_DELETE_PREDICATE,
|
||||
SKIP_DELETE_BITMAP,
|
||||
@ -1369,6 +1375,17 @@ public class SessionVariable implements Serializable, Writable {
|
||||
+ "considered outdated."})
|
||||
public int tableStatsHealthThreshold = 60;
|
||||
|
||||
@VariableMgr.VarAttr(name = ENABLE_MATERIALIZED_VIEW_REWRITE, needForward = true,
|
||||
description = {"是否开启基于结构信息的物化视图透明改写",
|
||||
"Whether to enable materialized view rewriting based on struct info"})
|
||||
public boolean enableMaterializedViewRewrite = false;
|
||||
|
||||
@VariableMgr.VarAttr(name = MATERIALIZED_VIEW_REWRITE_ENABLE_CONTAIN_FOREIGN_TABLE, needForward = true,
|
||||
description = {"基于结构信息的透明改写,是否使用包含外表的物化视图",
|
||||
"whether to use a materialized view that contains the foreign table "
|
||||
+ "when using rewriting based on struct info"})
|
||||
public boolean materializedViewRewriteEnableContainForeignTable = false;
|
||||
|
||||
public static final String IGNORE_RUNTIME_FILTER_IDS = "ignore_runtime_filter_ids";
|
||||
|
||||
public Set<Integer> getIgnoredRuntimeFilterIds() {
|
||||
@ -2956,4 +2973,12 @@ public class SessionVariable implements Serializable, Writable {
|
||||
public boolean isEnableInsertGroupCommit() {
|
||||
return enableInsertGroupCommit || Config.wait_internal_group_commit_finish;
|
||||
}
|
||||
|
||||
public boolean isEnableMaterializedViewRewrite() {
|
||||
return enableMaterializedViewRewrite;
|
||||
}
|
||||
|
||||
public boolean isMaterializedViewRewriteEnableContainForeignTable() {
|
||||
return materializedViewRewriteEnableContainForeignTable;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user