[feat](Nereids) support outer join and aggregate bitmap rewrite by mv (#28596)
- Support left outer join rewrite by materialized view - Support bitmap_union roll up to imp count(distinct) - Support partition materialized view rewrite
This commit is contained in:
@ -35,6 +35,7 @@ import org.apache.doris.mtmv.MTMVRefreshInfo;
|
||||
import org.apache.doris.mtmv.MTMVRelation;
|
||||
import org.apache.doris.mtmv.MTMVStatus;
|
||||
import org.apache.doris.persist.gson.GsonUtils;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
@ -128,10 +129,6 @@ public class MTMV extends OlapTable {
|
||||
return relation;
|
||||
}
|
||||
|
||||
public MTMVCache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void setCache(MTMVCache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
@ -202,12 +199,13 @@ public class MTMV extends OlapTable {
|
||||
return Sets.newHashSet(split);
|
||||
}
|
||||
|
||||
public MTMVCache getOrGenerateCache() throws AnalysisException {
|
||||
// this should use the same connectContext with query, to use the same session variable
|
||||
public MTMVCache getOrGenerateCache(ConnectContext parent) throws AnalysisException {
|
||||
if (cache == null) {
|
||||
writeMvLock();
|
||||
try {
|
||||
if (cache == null) {
|
||||
this.cache = MTMVCache.from(this, MTMVPlanUtil.createMTMVContext(this));
|
||||
this.cache = MTMVCache.from(this, parent);
|
||||
}
|
||||
} finally {
|
||||
writeMvUnlock();
|
||||
|
||||
@ -74,7 +74,7 @@ public class MTMVCache {
|
||||
? (Plan) ((LogicalResultSink) mvRewrittenPlan).child() : mvRewrittenPlan;
|
||||
// use rewritten plan output expression currently, if expression rewrite fail,
|
||||
// consider to use the analyzed plan for output expressions only
|
||||
List<NamedExpression> mvOutputExpressions = mvRewrittenPlan.getExpressions().stream()
|
||||
List<NamedExpression> mvOutputExpressions = mvPlan.getExpressions().stream()
|
||||
.map(NamedExpression.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
return new MTMVCache(mvPlan, mvOutputExpressions);
|
||||
|
||||
@ -30,6 +30,7 @@ import org.apache.doris.nereids.trees.plans.commands.info.RefreshMTMVInfo;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo;
|
||||
import org.apache.doris.persist.AlterMTMV;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
@ -49,7 +50,7 @@ public class MTMVRelationManager implements MTMVHookService {
|
||||
private Map<BaseTableInfo, Set<BaseTableInfo>> tableMTMVs = Maps.newConcurrentMap();
|
||||
|
||||
public Set<BaseTableInfo> getMtmvsByBaseTable(BaseTableInfo table) {
|
||||
return tableMTMVs.get(table);
|
||||
return tableMTMVs.getOrDefault(table, ImmutableSet.of());
|
||||
}
|
||||
|
||||
public Set<MTMV> getAvailableMTMVs(List<BaseTableInfo> tableInfos) {
|
||||
|
||||
@ -253,7 +253,7 @@ public class MTMVUtil {
|
||||
List<Partition> res = Lists.newArrayList();
|
||||
Collection<Partition> allPartitions = mtmv.getPartitions();
|
||||
// check session variable if enable rewrite
|
||||
if (!ctx.getSessionVariable().isEnableMvRewrite()) {
|
||||
if (!ctx.getSessionVariable().isEnableMaterializedViewRewrite()) {
|
||||
return res;
|
||||
}
|
||||
MTMVRelation mtmvRelation = mtmv.getRelation();
|
||||
@ -438,7 +438,7 @@ public class MTMVUtil {
|
||||
* @param relatedTable
|
||||
* @return mv.partitionId ==> relatedTable.partitionId
|
||||
*/
|
||||
private static Map<Long, Set<Long>> getMvToBasePartitions(MTMV mtmv, OlapTable relatedTable)
|
||||
public static Map<Long, Set<Long>> getMvToBasePartitions(MTMV mtmv, OlapTable relatedTable)
|
||||
throws AnalysisException {
|
||||
HashMap<Long, Set<Long>> res = Maps.newHashMap();
|
||||
Map<Long, PartitionItem> relatedTableItems = relatedTable.getPartitionInfo().getIdToItem(false);
|
||||
|
||||
@ -318,7 +318,9 @@ public class CascadesContext implements ScheduleContext {
|
||||
}
|
||||
|
||||
public List<MaterializationContext> getMaterializationContexts() {
|
||||
return materializationContexts;
|
||||
return materializationContexts.stream()
|
||||
.filter(MaterializationContext::isAvailable)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void addMaterializationContext(MaterializationContext materializationContext) {
|
||||
|
||||
@ -33,7 +33,6 @@ import java.util.Objects;
|
||||
* @see PlaceholderCollector
|
||||
*/
|
||||
public class PlaceholderExpression extends Expression implements AlwaysNotNullable {
|
||||
|
||||
private final Class<? extends Expression> delegateClazz;
|
||||
/**
|
||||
* 1 based
|
||||
|
||||
@ -40,7 +40,11 @@ import org.apache.doris.nereids.rules.exploration.join.PushDownProjectThroughSem
|
||||
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.MaterializedViewAggregateRule;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewFilterJoinRule;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewFilterProjectJoinRule;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewOnlyJoinRule;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectAggregateRule;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewProjectFilterJoinRule;
|
||||
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;
|
||||
@ -222,7 +226,11 @@ public class RuleSet {
|
||||
.build();
|
||||
|
||||
public static final List<Rule> MATERIALIZED_VIEW_RULES = planRuleFactories()
|
||||
.add(MaterializedViewOnlyJoinRule.INSTANCE)
|
||||
.add(MaterializedViewProjectJoinRule.INSTANCE)
|
||||
.add(MaterializedViewFilterJoinRule.INSTANCE)
|
||||
.add(MaterializedViewFilterProjectJoinRule.INSTANCE)
|
||||
.add(MaterializedViewProjectFilterJoinRule.INSTANCE)
|
||||
.add(MaterializedViewAggregateRule.INSTANCE)
|
||||
.add(MaterializedViewProjectAggregateRule.INSTANCE)
|
||||
.build();
|
||||
|
||||
@ -25,12 +25,16 @@ import org.apache.doris.nereids.jobs.joinorder.hypergraph.node.StructInfoNode;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.StructInfo.PlanSplitContext;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
|
||||
import org.apache.doris.nereids.trees.expressions.Any;
|
||||
import org.apache.doris.nereids.trees.expressions.ExprId;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.NamedExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.Slot;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Function;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.agg.BitmapUnion;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.agg.CouldRollUp;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
@ -39,8 +43,11 @@ import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -53,6 +60,17 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public abstract class AbstractMaterializedViewAggregateRule extends AbstractMaterializedViewRule {
|
||||
|
||||
protected static final Map<Expression, Expression>
|
||||
AGGREGATE_ROLL_UP_EQUIVALENT_FUNCTION_MAP = new HashMap<>();
|
||||
protected final String currentClassName = this.getClass().getSimpleName();
|
||||
|
||||
private final Logger logger = LogManager.getLogger(this.getClass());
|
||||
|
||||
static {
|
||||
AGGREGATE_ROLL_UP_EQUIVALENT_FUNCTION_MAP.put(new Count(true, Any.INSTANCE),
|
||||
new BitmapUnion(Any.INSTANCE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Plan rewriteQueryByView(MatchMode matchMode,
|
||||
StructInfo queryStructInfo,
|
||||
@ -63,10 +81,12 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
// get view and query aggregate and top plan correspondingly
|
||||
Pair<Plan, LogicalAggregate<Plan>> viewTopPlanAndAggPair = splitToTopPlanAndAggregate(viewStructInfo);
|
||||
if (viewTopPlanAndAggPair == null) {
|
||||
logger.warn(currentClassName + " split to view to top plan and agg fail so return null");
|
||||
return null;
|
||||
}
|
||||
Pair<Plan, LogicalAggregate<Plan>> queryTopPlanAndAggPair = splitToTopPlanAndAggregate(queryStructInfo);
|
||||
if (queryTopPlanAndAggPair == null) {
|
||||
logger.warn(currentClassName + " split to query to top plan and agg fail so return null");
|
||||
return null;
|
||||
}
|
||||
// Firstly, handle query group by expression rewrite
|
||||
@ -88,13 +108,14 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
needRollUp = !queryGroupShuttledExpression.equals(viewGroupShuttledExpression);
|
||||
}
|
||||
if (!needRollUp) {
|
||||
List<Expression> rewrittenQueryGroupExpr = rewriteExpression(queryTopPlan.getOutput(),
|
||||
List<Expression> rewrittenQueryGroupExpr = rewriteExpression(queryTopPlan.getExpressions(),
|
||||
queryTopPlan,
|
||||
materializationContext.getMvExprToMvScanExprMapping(),
|
||||
queryToViewSlotMapping,
|
||||
true);
|
||||
if (rewrittenQueryGroupExpr == null) {
|
||||
if (rewrittenQueryGroupExpr.isEmpty()) {
|
||||
// can not rewrite, bail out.
|
||||
logger.debug(currentClassName + " can not rewrite expression when not need roll up");
|
||||
return null;
|
||||
}
|
||||
return new LogicalProject<>(
|
||||
@ -109,12 +130,14 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
viewExpr -> viewExpr.anyMatch(expr -> expr instanceof AggregateFunction
|
||||
&& ((AggregateFunction) expr).isDistinct()))) {
|
||||
// if mv aggregate function contains distinct, can not roll up, bail out.
|
||||
logger.debug(currentClassName + " view contains distinct function so can not roll up");
|
||||
return null;
|
||||
}
|
||||
// split the query top plan expressions to group expressions and functions, if can not, bail out.
|
||||
Pair<Set<? extends Expression>, Set<? extends Expression>> queryGroupAndFunctionPair
|
||||
= topPlanSplitToGroupAndFunction(queryTopPlanAndAggPair);
|
||||
if (queryGroupAndFunctionPair == null) {
|
||||
logger.warn(currentClassName + " query top plan split to group by and function fail so return null");
|
||||
return null;
|
||||
}
|
||||
// Secondly, try to roll up the agg functions
|
||||
@ -132,23 +155,19 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
for (Expression topExpression : queryTopPlan.getExpressions()) {
|
||||
// is agg function, try to roll up and rewrite
|
||||
if (queryTopPlanFunctionSet.contains(topExpression)) {
|
||||
Expression needRollupShuttledExpr = ExpressionUtils.shuttleExpressionWithLineage(
|
||||
Expression queryFunctionShuttled = ExpressionUtils.shuttleExpressionWithLineage(
|
||||
topExpression,
|
||||
queryTopPlan);
|
||||
if (!mvExprToMvScanExprQueryBased.containsKey(needRollupShuttledExpr)) {
|
||||
// function can not rewrite by view
|
||||
return null;
|
||||
}
|
||||
// try to roll up
|
||||
AggregateFunction needRollupAggFunction = (AggregateFunction) topExpression.firstMatch(
|
||||
AggregateFunction queryFunction = (AggregateFunction) topExpression.firstMatch(
|
||||
expr -> expr instanceof AggregateFunction);
|
||||
AggregateFunction rollupAggregateFunction = rollup(needRollupAggFunction,
|
||||
mvExprToMvScanExprQueryBased.get(needRollupShuttledExpr));
|
||||
Function rollupAggregateFunction = rollup(queryFunction, queryFunctionShuttled,
|
||||
mvExprToMvScanExprQueryBased);
|
||||
if (rollupAggregateFunction == null) {
|
||||
return null;
|
||||
}
|
||||
// key is query need roll up expr, value is mv scan based roll up expr
|
||||
needRollupExprMap.put(needRollupShuttledExpr, rollupAggregateFunction);
|
||||
needRollupExprMap.put(queryFunctionShuttled, rollupAggregateFunction);
|
||||
// rewrite query function expression by mv expression
|
||||
Expression rewrittenFunctionExpression = rewriteExpression(topExpression,
|
||||
queryTopPlan,
|
||||
@ -156,6 +175,7 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
queryToViewSlotMapping,
|
||||
false);
|
||||
if (rewrittenFunctionExpression == null) {
|
||||
logger.debug(currentClassName + " roll up expression can not rewrite by view so return null");
|
||||
return null;
|
||||
}
|
||||
finalAggregateExpressions.add((NamedExpression) rewrittenFunctionExpression);
|
||||
@ -165,6 +185,8 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
ExpressionUtils.shuttleExpressionWithLineage(topExpression, queryTopPlan);
|
||||
if (!mvExprToMvScanExprQueryBased.containsKey(queryGroupShuttledExpr)) {
|
||||
// group expr can not rewrite by view
|
||||
logger.debug(currentClassName
|
||||
+ " view group expressions can not contains the query group by expression so return null");
|
||||
return null;
|
||||
}
|
||||
groupRewrittenExprMap.put(queryGroupShuttledExpr,
|
||||
@ -177,6 +199,8 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
queryToViewSlotMapping,
|
||||
true);
|
||||
if (rewrittenGroupExpression == null) {
|
||||
logger.debug(currentClassName
|
||||
+ " query top expression can not be rewritten by view so return null");
|
||||
return null;
|
||||
}
|
||||
finalAggregateExpressions.add((NamedExpression) rewrittenGroupExpression);
|
||||
@ -226,17 +250,33 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
}
|
||||
|
||||
// only support sum roll up, support other agg functions later.
|
||||
private AggregateFunction rollup(AggregateFunction originFunction,
|
||||
Expression mappedExpression) {
|
||||
Class<? extends AggregateFunction> rollupAggregateFunction = originFunction.getRollup();
|
||||
if (rollupAggregateFunction == null) {
|
||||
private Function rollup(AggregateFunction queryFunction,
|
||||
Expression queryFunctionShuttled,
|
||||
Map<Expression, Expression> mvExprToMvScanExprQueryBased) {
|
||||
if (!(queryFunction instanceof CouldRollUp)) {
|
||||
return null;
|
||||
}
|
||||
if (Sum.class.isAssignableFrom(rollupAggregateFunction)) {
|
||||
return new Sum(originFunction.isDistinct(), mappedExpression);
|
||||
Expression rollupParam = null;
|
||||
if (mvExprToMvScanExprQueryBased.containsKey(queryFunctionShuttled)) {
|
||||
// function can rewrite by view
|
||||
rollupParam = mvExprToMvScanExprQueryBased.get(queryFunctionShuttled);
|
||||
} else {
|
||||
// function can not rewrite by view, try to use complex roll up param
|
||||
// eg: query is count(distinct param), mv sql is bitmap_union(to_bitmap(param))
|
||||
for (Expression mvExprShuttled : mvExprToMvScanExprQueryBased.keySet()) {
|
||||
if (!(mvExprShuttled instanceof Function)) {
|
||||
continue;
|
||||
}
|
||||
if (isAggregateFunctionEquivalent(queryFunction, (Function) mvExprShuttled)) {
|
||||
rollupParam = mvExprToMvScanExprQueryBased.get(mvExprShuttled);
|
||||
}
|
||||
}
|
||||
}
|
||||
// can rollup return null
|
||||
return null;
|
||||
if (rollupParam == null) {
|
||||
return null;
|
||||
}
|
||||
// do roll up
|
||||
return ((CouldRollUp) queryFunction).constructRollUp(rollupParam);
|
||||
}
|
||||
|
||||
private Pair<Set<? extends Expression>, Set<? extends Expression>> topPlanSplitToGroupAndFunction(
|
||||
@ -306,4 +346,23 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isAggregateFunctionEquivalent(Function queryFunction, Function viewFunction) {
|
||||
if (queryFunction.equals(viewFunction)) {
|
||||
return true;
|
||||
}
|
||||
// get query equivalent function
|
||||
Expression equivalentFunction = null;
|
||||
for (Map.Entry<Expression, Expression> entry : AGGREGATE_ROLL_UP_EQUIVALENT_FUNCTION_MAP.entrySet()) {
|
||||
if (entry.getKey().equals(queryFunction)) {
|
||||
equivalentFunction = entry.getValue();
|
||||
}
|
||||
}
|
||||
// check is have equivalent function or not
|
||||
if (equivalentFunction == null) {
|
||||
return false;
|
||||
}
|
||||
// current compare
|
||||
return equivalentFunction.equals(viewFunction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,9 @@ 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 org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -35,6 +38,10 @@ import java.util.stream.Collectors;
|
||||
* This is responsible for common join rewriting
|
||||
*/
|
||||
public abstract class AbstractMaterializedViewJoinRule extends AbstractMaterializedViewRule {
|
||||
|
||||
protected final String currentClassName = this.getClass().getSimpleName();
|
||||
private final Logger logger = LogManager.getLogger(this.getClass());
|
||||
|
||||
@Override
|
||||
protected Plan rewriteQueryByView(MatchMode matchMode,
|
||||
StructInfo queryStructInfo,
|
||||
@ -53,6 +60,7 @@ public abstract class AbstractMaterializedViewJoinRule extends AbstractMateriali
|
||||
// Can not rewrite, bail out
|
||||
if (expressionsRewritten.isEmpty()
|
||||
|| expressionsRewritten.stream().anyMatch(expr -> !(expr instanceof NamedExpression))) {
|
||||
logger.warn(currentClassName + " expression to rewrite is not named expr so return null");
|
||||
return null;
|
||||
}
|
||||
// record the group id in materializationContext, and when rewrite again in
|
||||
|
||||
@ -17,8 +17,21 @@
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.catalog.MTMV;
|
||||
import org.apache.doris.catalog.OlapTable;
|
||||
import org.apache.doris.catalog.Partition;
|
||||
import org.apache.doris.catalog.PartitionInfo;
|
||||
import org.apache.doris.catalog.PartitionItem;
|
||||
import org.apache.doris.catalog.PartitionType;
|
||||
import org.apache.doris.catalog.TableIf;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.mtmv.BaseTableInfo;
|
||||
import org.apache.doris.mtmv.MTMVCache;
|
||||
import org.apache.doris.mtmv.MTMVPartitionInfo;
|
||||
import org.apache.doris.mtmv.MTMVUtil;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.jobs.executor.Rewriter;
|
||||
import org.apache.doris.nereids.rules.exploration.ExplorationRuleFactory;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.mapping.EquivalenceClassSetMapping;
|
||||
import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping;
|
||||
@ -36,26 +49,32 @@ import org.apache.doris.nereids.trees.plans.JoinType;
|
||||
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.trees.plans.logical.LogicalOlapScan;
|
||||
import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The abstract class for all materialized view rules
|
||||
*/
|
||||
public abstract class AbstractMaterializedViewRule {
|
||||
|
||||
public abstract class AbstractMaterializedViewRule implements ExplorationRuleFactory {
|
||||
public static final HashSet<JoinType> SUPPORTED_JOIN_TYPE_SET =
|
||||
Sets.newHashSet(JoinType.INNER_JOIN, JoinType.LEFT_OUTER_JOIN);
|
||||
protected final String currentClassName = this.getClass().getSimpleName();
|
||||
private final Logger logger = LogManager.getLogger(this.getClass());
|
||||
|
||||
/**
|
||||
* The abstract template method for query rewrite, it contains the main logic and different query
|
||||
@ -65,6 +84,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
List<MaterializationContext> materializationContexts = cascadesContext.getMaterializationContexts();
|
||||
List<Plan> rewriteResults = new ArrayList<>();
|
||||
if (materializationContexts.isEmpty()) {
|
||||
logger.info(currentClassName + " materializationContexts is empty so return");
|
||||
return rewriteResults;
|
||||
}
|
||||
|
||||
@ -72,6 +92,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
// TODO Just Check query queryPlan firstly, support multi later.
|
||||
StructInfo queryStructInfo = queryStructInfos.get(0);
|
||||
if (!checkPattern(queryStructInfo)) {
|
||||
logger.info(currentClassName + " queryStructInfo is not valid so return");
|
||||
return rewriteResults;
|
||||
}
|
||||
|
||||
@ -80,40 +101,51 @@ public abstract class AbstractMaterializedViewRule {
|
||||
if (queryPlan.getGroupExpression().isPresent()
|
||||
&& materializationContext.alreadyRewrite(
|
||||
queryPlan.getGroupExpression().get().getOwnerGroup().getGroupId())) {
|
||||
logger.info(currentClassName + " this group is already rewritten so skip");
|
||||
continue;
|
||||
}
|
||||
Plan mvPlan = materializationContext.getMtmv().getCache().getLogicalPlan();
|
||||
List<StructInfo> viewStructInfos = extractStructInfo(mvPlan, cascadesContext);
|
||||
MTMV mtmv = materializationContext.getMTMV();
|
||||
MTMVCache mtmvCache = getCacheFromMTMV(mtmv, cascadesContext);
|
||||
if (mtmvCache == null) {
|
||||
logger.info(currentClassName + " mv cache is null so return");
|
||||
return rewriteResults;
|
||||
}
|
||||
List<StructInfo> viewStructInfos = extractStructInfo(mtmvCache.getLogicalPlan(), cascadesContext);
|
||||
if (viewStructInfos.size() > 1) {
|
||||
// view struct info should only have one
|
||||
logger.info(currentClassName + " the num of view struct info is more then one so return");
|
||||
return rewriteResults;
|
||||
}
|
||||
StructInfo viewStructInfo = viewStructInfos.get(0);
|
||||
if (!checkPattern(viewStructInfo)) {
|
||||
logger.info(currentClassName + " viewStructInfo is not valid so return");
|
||||
continue;
|
||||
}
|
||||
MatchMode matchMode = decideMatchMode(queryStructInfo.getRelations(), viewStructInfo.getRelations());
|
||||
if (MatchMode.COMPLETE != matchMode) {
|
||||
logger.info(currentClassName + " match mode is not complete so return");
|
||||
continue;
|
||||
}
|
||||
List<RelationMapping> queryToViewTableMappings =
|
||||
RelationMapping.generate(queryStructInfo.getRelations(), viewStructInfo.getRelations());
|
||||
// if any relation in query and view can not map, bail out.
|
||||
if (queryToViewTableMappings == null) {
|
||||
logger.info(currentClassName + " query to view table mapping null so return");
|
||||
return rewriteResults;
|
||||
}
|
||||
for (RelationMapping queryToViewTableMapping : queryToViewTableMappings) {
|
||||
SlotMapping queryToViewSlotMapping = SlotMapping.generate(queryToViewTableMapping);
|
||||
if (queryToViewSlotMapping == null) {
|
||||
logger.info(currentClassName + " query to view slot mapping null so continue");
|
||||
continue;
|
||||
}
|
||||
LogicalCompatibilityContext compatibilityContext =
|
||||
LogicalCompatibilityContext.from(queryToViewTableMapping, queryToViewSlotMapping,
|
||||
queryStructInfo, viewStructInfo);
|
||||
// todo outer join compatibility check
|
||||
List<Expression> pulledUpExpressions = StructInfo.isGraphLogicalEquals(queryStructInfo, viewStructInfo,
|
||||
compatibilityContext);
|
||||
if (pulledUpExpressions == null) {
|
||||
logger.info(currentClassName + " graph logical is not equals so continue");
|
||||
continue;
|
||||
}
|
||||
// set pulled up expression to queryStructInfo predicates and update related predicates
|
||||
@ -124,6 +156,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
queryToViewSlotMapping);
|
||||
// Can not compensate, bail out
|
||||
if (compensatePredicates.isEmpty()) {
|
||||
logger.info(currentClassName + " predicate compensate fail so continue");
|
||||
continue;
|
||||
}
|
||||
Plan rewritedPlan;
|
||||
@ -139,6 +172,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
queryToViewSlotMapping,
|
||||
true);
|
||||
if (rewriteCompensatePredicates.isEmpty()) {
|
||||
logger.info(currentClassName + " compensate predicate rewrite by view fail so continue");
|
||||
continue;
|
||||
}
|
||||
rewritedPlan = new LogicalFilter<>(Sets.newHashSet(rewriteCompensatePredicates), mvScan);
|
||||
@ -151,14 +185,108 @@ public abstract class AbstractMaterializedViewRule {
|
||||
rewritedPlan,
|
||||
materializationContext);
|
||||
if (rewritedPlan == null) {
|
||||
logger.info(currentClassName + " rewrite query by view fail so continue");
|
||||
continue;
|
||||
}
|
||||
if (!checkPartitionIsValid(queryStructInfo, materializationContext, cascadesContext)) {
|
||||
logger.info(currentClassName + " check partition validation fail so continue");
|
||||
continue;
|
||||
}
|
||||
// run rbo job on mv rewritten plan
|
||||
CascadesContext rewrittenPlanContext =
|
||||
CascadesContext.initContext(cascadesContext.getStatementContext(), rewritedPlan,
|
||||
cascadesContext.getCurrentJobContext().getRequiredProperties());
|
||||
Rewriter.getWholeTreeRewriter(cascadesContext).execute();
|
||||
rewritedPlan = rewrittenPlanContext.getRewritePlan();
|
||||
logger.info(currentClassName + "rewrite by materialized view success");
|
||||
rewriteResults.add(rewritedPlan);
|
||||
}
|
||||
}
|
||||
return rewriteResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partition will be pruned in query then add the record the partitions to select partitions on
|
||||
* catalog relation.
|
||||
* Maybe only just some partitions is valid in materialized view, so we should check if the mv can
|
||||
* offer the partitions which query used or not.
|
||||
*/
|
||||
protected boolean checkPartitionIsValid(
|
||||
StructInfo queryInfo,
|
||||
MaterializationContext materializationContext,
|
||||
CascadesContext cascadesContext) {
|
||||
// check partition is valid or not
|
||||
MTMV mtmv = materializationContext.getMTMV();
|
||||
PartitionInfo mvPartitionInfo = mtmv.getPartitionInfo();
|
||||
if (PartitionType.UNPARTITIONED.equals(mvPartitionInfo.getType())) {
|
||||
// if not partition, if rewrite success, it means mv is available
|
||||
return true;
|
||||
}
|
||||
// check mv related table partition is valid or not
|
||||
MTMVPartitionInfo mvCustomPartitionInfo = mtmv.getMvPartitionInfo();
|
||||
BaseTableInfo relatedPartitionTable = mvCustomPartitionInfo.getRelatedTable();
|
||||
if (relatedPartitionTable == null) {
|
||||
return true;
|
||||
}
|
||||
Optional<LogicalOlapScan> relatedTableRelation = queryInfo.getRelations().stream()
|
||||
.filter(LogicalOlapScan.class::isInstance)
|
||||
.filter(relation -> relatedPartitionTable.equals(new BaseTableInfo(relation.getTable())))
|
||||
.map(LogicalOlapScan.class::cast)
|
||||
.findFirst();
|
||||
if (!relatedTableRelation.isPresent()) {
|
||||
logger.warn("mv is partition update, but related table relation is null");
|
||||
return false;
|
||||
}
|
||||
OlapTable relatedTable = relatedTableRelation.get().getTable();
|
||||
Map<Long, Set<Long>> mvToBasePartitionMap;
|
||||
try {
|
||||
mvToBasePartitionMap = MTMVUtil.getMvToBasePartitions(mtmv, relatedTable);
|
||||
} catch (AnalysisException e) {
|
||||
logger.warn("mvRewriteSuccess getMvToBasePartitions fail", e);
|
||||
return false;
|
||||
}
|
||||
// get mv valid partitions
|
||||
Collection<Partition> mvDataValidPartitions = MTMVUtil.getMTMVCanRewritePartitions(mtmv,
|
||||
cascadesContext.getConnectContext());
|
||||
Map<Long, PartitionItem> allPartitions = mvPartitionInfo.getAllPartitions();
|
||||
if (!allPartitions.isEmpty() && mvDataValidPartitions.isEmpty()) {
|
||||
// do not have valid partition
|
||||
return false;
|
||||
}
|
||||
// get mv related table valid partitions
|
||||
Set<Long> relatedTalbeValidSet = mvDataValidPartitions.stream()
|
||||
.map(partition -> {
|
||||
Set<Long> relatedBaseTablePartitions = mvToBasePartitionMap.get(partition.getId());
|
||||
if (relatedBaseTablePartitions == null || relatedBaseTablePartitions.isEmpty()) {
|
||||
return ImmutableList.of();
|
||||
} else {
|
||||
return relatedBaseTablePartitions;
|
||||
}
|
||||
})
|
||||
.flatMap(Collection::stream)
|
||||
.map(Long.class::cast)
|
||||
.collect(Collectors.toSet());
|
||||
// get query selected partitions to make the partitions is valid or not
|
||||
Set<Long> relatedTableSelectedPartitionToCheck =
|
||||
new HashSet<>(relatedTableRelation.get().getSelectedPartitionIds());
|
||||
if (relatedTableSelectedPartitionToCheck.isEmpty()) {
|
||||
relatedTableSelectedPartitionToCheck.addAll(relatedTable.getPartitionIds());
|
||||
}
|
||||
return !relatedTalbeValidSet.isEmpty()
|
||||
&& relatedTalbeValidSet.containsAll(relatedTableSelectedPartitionToCheck);
|
||||
}
|
||||
|
||||
private MTMVCache getCacheFromMTMV(MTMV mtmv, CascadesContext cascadesContext) {
|
||||
MTMVCache cache;
|
||||
try {
|
||||
cache = mtmv.getOrGenerateCache(cascadesContext.getConnectContext());
|
||||
} catch (AnalysisException analysisException) {
|
||||
logger.warn(this.getClass().getSimpleName() + " get mtmv cache analysisException", analysisException);
|
||||
return null;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite query by view, for aggregate or join rewriting should be different inherit class implementation
|
||||
*/
|
||||
@ -268,6 +396,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
.toSlotReferenceMap();
|
||||
EquivalenceClass viewEquivalenceClassQueryBased = viewEquivalenceClass.permute(viewToQuerySlotMapping);
|
||||
if (viewEquivalenceClassQueryBased == null) {
|
||||
logger.info(currentClassName + " permute view equivalence class by query fail so return empty");
|
||||
return SplitPredicate.empty();
|
||||
}
|
||||
final List<Expression> equalCompensateConjunctions = new ArrayList<>();
|
||||
@ -276,6 +405,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
}
|
||||
if (queryEquivalenceClass.isEmpty()
|
||||
&& !viewEquivalenceClass.isEmpty()) {
|
||||
logger.info(currentClassName + " view has equivalence class but query not so return empty");
|
||||
return SplitPredicate.empty();
|
||||
}
|
||||
EquivalenceClassSetMapping queryToViewEquivalenceMapping =
|
||||
@ -283,6 +413,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
// can not map all target equivalence class, can not compensate
|
||||
if (queryToViewEquivalenceMapping.getEquivalenceClassSetMap().size()
|
||||
< viewEquivalenceClass.getEquivalenceSetList().size()) {
|
||||
logger.info(currentClassName + " view has more equivalence than query so return empty");
|
||||
return SplitPredicate.empty();
|
||||
}
|
||||
// do equal compensate
|
||||
@ -330,6 +461,7 @@ public abstract class AbstractMaterializedViewRule {
|
||||
// query range predicate can not contain all view range predicate when view have range predicate, bail out
|
||||
if (!viewRangePredicateQueryBased.equals(BooleanLiteral.TRUE)
|
||||
&& !queryRangeSet.containsAll(viewRangeQueryBasedSet)) {
|
||||
logger.info(currentClassName + " query range predicate set can not contains all view range predicate");
|
||||
return SplitPredicate.empty();
|
||||
}
|
||||
queryRangeSet.removeAll(viewRangeQueryBasedSet);
|
||||
@ -349,6 +481,8 @@ public abstract class AbstractMaterializedViewRule {
|
||||
// bail out
|
||||
if (!viewResidualPredicateQueryBased.equals(BooleanLiteral.TRUE)
|
||||
&& !queryResidualSet.containsAll(viewResidualQueryBasedSet)) {
|
||||
logger.info(
|
||||
currentClassName + " query residual predicate set can not contains all view residual predicate");
|
||||
return SplitPredicate.empty();
|
||||
}
|
||||
queryResidualSet.removeAll(viewResidualQueryBasedSet);
|
||||
|
||||
@ -19,12 +19,8 @@ package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.catalog.Env;
|
||||
import org.apache.doris.catalog.MTMV;
|
||||
import org.apache.doris.catalog.OlapTable;
|
||||
import org.apache.doris.catalog.TableIf;
|
||||
import org.apache.doris.catalog.TableIf.TableType;
|
||||
import org.apache.doris.common.MetaNotFoundException;
|
||||
import org.apache.doris.mtmv.BaseTableInfo;
|
||||
import org.apache.doris.mtmv.MTMVRelationManager;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.NereidsPlanner;
|
||||
import org.apache.doris.nereids.PlannerHook;
|
||||
@ -42,13 +38,14 @@ import com.google.common.collect.Sets;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** If enable query rewrite with mv, should init materialization context after analyze*/
|
||||
/**
|
||||
* If enable query rewrite with mv, should init materialization context after analyze
|
||||
*/
|
||||
public class InitMaterializationContextHook implements PlannerHook {
|
||||
|
||||
public static final Logger LOG = LogManager.getLogger(InitMaterializationContextHook.class);
|
||||
@ -60,7 +57,9 @@ public class InitMaterializationContextHook implements PlannerHook {
|
||||
}
|
||||
|
||||
private void initMaterializationContext(CascadesContext cascadesContext) {
|
||||
|
||||
if (!cascadesContext.getConnectContext().getSessionVariable().isEnableMaterializedViewRewrite()) {
|
||||
return;
|
||||
}
|
||||
Plan rewritePlan = cascadesContext.getRewritePlan();
|
||||
TableCollectorContext collectorContext = new TableCollectorContext(Sets.newHashSet());
|
||||
rewritePlan.accept(TableCollector.INSTANCE, collectorContext);
|
||||
@ -68,48 +67,30 @@ public class InitMaterializationContextHook implements PlannerHook {
|
||||
if (collectedTables.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<BaseTableInfo> baseTableUsed =
|
||||
List<BaseTableInfo> usedBaseTables =
|
||||
collectedTables.stream().map(BaseTableInfo::new).collect(Collectors.toList());
|
||||
// TODO the logic should be move to MTMVRelationManager later when getAvailableMaterializedView is ready in
|
||||
// MV Cache manager
|
||||
Env env = cascadesContext.getConnectContext().getEnv();
|
||||
MTMVRelationManager cacheManager = env.getMtmvService().getRelationManager();
|
||||
Set<BaseTableInfo> materializedViews = new HashSet<>();
|
||||
for (BaseTableInfo baseTableInfo : baseTableUsed) {
|
||||
Set<BaseTableInfo> mtmvsByBaseTable = cacheManager.getMtmvsByBaseTable(baseTableInfo);
|
||||
if (mtmvsByBaseTable == null || mtmvsByBaseTable.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
materializedViews.addAll(mtmvsByBaseTable);
|
||||
}
|
||||
if (materializedViews.isEmpty()) {
|
||||
Set<MTMV> availableMTMVs = Env.getCurrentEnv().getMtmvService().getRelationManager()
|
||||
.getAvailableMTMVs(usedBaseTables);
|
||||
if (availableMTMVs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
materializedViews.forEach(mvBaseTableInfo -> {
|
||||
try {
|
||||
MTMV materializedView = (MTMV) Env.getCurrentInternalCatalog()
|
||||
.getDbOrMetaException(mvBaseTableInfo.getDbId())
|
||||
.getTableOrMetaException(mvBaseTableInfo.getTableId(), TableType.MATERIALIZED_VIEW);
|
||||
|
||||
// generate outside, maybe add partition filter in the future
|
||||
LogicalOlapScan mvScan = new LogicalOlapScan(
|
||||
cascadesContext.getStatementContext().getNextRelationId(),
|
||||
(OlapTable) materializedView,
|
||||
ImmutableList.of(materializedView.getQualifiedDbName()),
|
||||
// this must be empty, or it will be used to sample
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Optional.empty());
|
||||
mvScan = mvScan.withMaterializedIndexSelected(PreAggStatus.on(), materializedView.getBaseIndexId());
|
||||
List<NamedExpression> mvProjects = mvScan.getOutput().stream().map(NamedExpression.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
// todo should force keep consistency to mv sql plan output
|
||||
Plan projectScan = new LogicalProject<Plan>(mvProjects, mvScan);
|
||||
cascadesContext.addMaterializationContext(
|
||||
MaterializationContext.fromMaterializedView(materializedView, projectScan, cascadesContext));
|
||||
} catch (MetaNotFoundException metaNotFoundException) {
|
||||
LOG.error(mvBaseTableInfo.toString() + " can not find corresponding materialized view.");
|
||||
}
|
||||
availableMTMVs.forEach(materializedView -> {
|
||||
// generate outside, maybe add partition filter in the future
|
||||
LogicalOlapScan mvScan = new LogicalOlapScan(
|
||||
cascadesContext.getStatementContext().getNextRelationId(),
|
||||
materializedView,
|
||||
ImmutableList.of(materializedView.getQualifiedDbName()),
|
||||
// this must be empty, or it will be used to sample
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Optional.empty());
|
||||
mvScan = mvScan.withMaterializedIndexSelected(PreAggStatus.on(), materializedView.getBaseIndexId());
|
||||
List<NamedExpression> mvProjects = mvScan.getOutput().stream().map(NamedExpression.class::cast)
|
||||
.collect(Collectors.toList());
|
||||
// todo should force keep consistency to mv sql plan output
|
||||
Plan projectScan = new LogicalProject<Plan>(mvProjects, mvScan);
|
||||
cascadesContext.addMaterializationContext(
|
||||
MaterializationContext.fromMaterializedView(materializedView, projectScan, cascadesContext));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.catalog.MTMV;
|
||||
import org.apache.doris.catalog.Table;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
import org.apache.doris.mtmv.MTMVCache;
|
||||
import org.apache.doris.nereids.CascadesContext;
|
||||
import org.apache.doris.nereids.memo.GroupId;
|
||||
@ -27,6 +28,8 @@ import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.util.ExpressionUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -37,6 +40,7 @@ import java.util.Set;
|
||||
*/
|
||||
public class MaterializationContext {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(MaterializationContext.class);
|
||||
private MTMV mtmv;
|
||||
// Should use stmt id generator in query context
|
||||
private final Plan mvScanPlan;
|
||||
@ -46,6 +50,7 @@ public class MaterializationContext {
|
||||
private final Set<GroupId> matchedGroups = new HashSet<>();
|
||||
// generate form mv scan plan
|
||||
private ExpressionMapping mvExprToMvScanExprMapping;
|
||||
private boolean available = true;
|
||||
|
||||
/**
|
||||
* MaterializationContext, this contains necessary info for query rewriting by mv
|
||||
@ -59,11 +64,16 @@ public class MaterializationContext {
|
||||
this.mvScanPlan = mvScanPlan;
|
||||
this.baseTables = baseTables;
|
||||
this.baseViews = baseViews;
|
||||
MTMVCache mtmvCache = mtmv.getCache();
|
||||
// TODO This logic should move to materialized view cache manager
|
||||
|
||||
MTMVCache mtmvCache = null;
|
||||
try {
|
||||
mtmvCache = mtmv.getOrGenerateCache(cascadesContext.getConnectContext());
|
||||
} catch (AnalysisException e) {
|
||||
LOG.warn("MaterializationContext init mv cache generate fail", e);
|
||||
}
|
||||
if (mtmvCache == null) {
|
||||
mtmvCache = mtmvCache.from(mtmv, cascadesContext.getConnectContext());
|
||||
mtmv.setCache(mtmvCache);
|
||||
this.available = false;
|
||||
return;
|
||||
}
|
||||
// mv output expression shuttle, this will be used to expression rewrite
|
||||
this.mvExprToMvScanExprMapping = ExpressionMapping.generate(
|
||||
@ -85,7 +95,7 @@ public class MaterializationContext {
|
||||
matchedGroups.add(groupId);
|
||||
}
|
||||
|
||||
public MTMV getMtmv() {
|
||||
public MTMV getMTMV() {
|
||||
return mtmv;
|
||||
}
|
||||
|
||||
@ -105,6 +115,10 @@ public class MaterializationContext {
|
||||
return mvExprToMvScanExprMapping;
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
/**
|
||||
* MaterializationContext fromMaterializedView
|
||||
*/
|
||||
|
||||
@ -18,9 +18,7 @@
|
||||
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.LogicalAggregate;
|
||||
|
||||
@ -31,7 +29,7 @@ import java.util.List;
|
||||
/**
|
||||
* This is responsible for aggregate rewriting according to different pattern
|
||||
* */
|
||||
public class MaterializedViewAggregateRule extends AbstractMaterializedViewAggregateRule implements RewriteRuleFactory {
|
||||
public class MaterializedViewAggregateRule extends AbstractMaterializedViewAggregateRule {
|
||||
|
||||
public static final MaterializedViewAggregateRule INSTANCE = new MaterializedViewAggregateRule();
|
||||
|
||||
@ -41,6 +39,6 @@ public class MaterializedViewAggregateRule extends AbstractMaterializedViewAggre
|
||||
logicalAggregate(any()).thenApplyMulti(ctx -> {
|
||||
LogicalAggregate<Plan> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_ONLY_AGGREGATE, RulePromise.EXPLORE));
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_ONLY_AGGREGATE));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is responsible for join pattern such as filter on join
|
||||
*/
|
||||
public class MaterializedViewFilterJoinRule extends AbstractMaterializedViewJoinRule {
|
||||
|
||||
public static final MaterializedViewFilterJoinRule INSTANCE = new MaterializedViewFilterJoinRule();
|
||||
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return ImmutableList.of(
|
||||
logicalFilter(logicalJoin(any(), any())).thenApplyMulti(ctx -> {
|
||||
LogicalFilter<LogicalJoin<Plan, Plan>> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_FILTER_JOIN));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
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 pattern such as filter on project on join
|
||||
*/
|
||||
public class MaterializedViewFilterProjectJoinRule extends AbstractMaterializedViewJoinRule {
|
||||
|
||||
public static final MaterializedViewFilterProjectJoinRule INSTANCE = new MaterializedViewFilterProjectJoinRule();
|
||||
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return ImmutableList.of(
|
||||
logicalFilter(logicalProject(logicalJoin(any(), any()))).thenApplyMulti(ctx -> {
|
||||
LogicalFilter<LogicalProject<LogicalJoin<Plan, Plan>>> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_FILTER_PROJECT_JOIN));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is responsible for join pattern such as only join
|
||||
*/
|
||||
public class MaterializedViewOnlyJoinRule extends AbstractMaterializedViewJoinRule {
|
||||
|
||||
public static final MaterializedViewOnlyJoinRule INSTANCE = new MaterializedViewOnlyJoinRule();
|
||||
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return ImmutableList.of(
|
||||
logicalJoin(any(), any()).thenApplyMulti(ctx -> {
|
||||
LogicalJoin<Plan, Plan> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_ONLY_JOIN));
|
||||
}
|
||||
}
|
||||
@ -18,9 +18,7 @@
|
||||
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.LogicalAggregate;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
|
||||
@ -30,8 +28,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
||||
/**MaterializedViewProjectAggregateRule*/
|
||||
public class MaterializedViewProjectAggregateRule extends AbstractMaterializedViewAggregateRule implements
|
||||
RewriteRuleFactory {
|
||||
public class MaterializedViewProjectAggregateRule extends AbstractMaterializedViewAggregateRule {
|
||||
|
||||
public static final MaterializedViewProjectAggregateRule INSTANCE = new MaterializedViewProjectAggregateRule();
|
||||
|
||||
@ -41,6 +38,6 @@ public class MaterializedViewProjectAggregateRule extends AbstractMaterializedVi
|
||||
logicalProject(logicalAggregate(any())).thenApplyMulti(ctx -> {
|
||||
LogicalProject<LogicalAggregate<Plan>> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_PROJECT_AGGREGATE, RulePromise.EXPLORE));
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_PROJECT_AGGREGATE));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.rules.exploration.mv;
|
||||
|
||||
import org.apache.doris.nereids.rules.Rule;
|
||||
import org.apache.doris.nereids.rules.RuleType;
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
|
||||
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 pattern such as project on filter on join
|
||||
*/
|
||||
public class MaterializedViewProjectFilterJoinRule extends AbstractMaterializedViewJoinRule {
|
||||
|
||||
public static final MaterializedViewProjectFilterJoinRule INSTANCE = new MaterializedViewProjectFilterJoinRule();
|
||||
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
return ImmutableList.of(
|
||||
logicalProject(logicalFilter(logicalJoin(any(), any()))).thenApplyMulti(ctx -> {
|
||||
LogicalProject<LogicalFilter<LogicalJoin<Plan, Plan>>> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_PROJECT_FILTER_JOIN));
|
||||
}
|
||||
}
|
||||
@ -18,9 +18,7 @@
|
||||
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;
|
||||
@ -30,9 +28,9 @@ import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is responsible for join rewriting according to different pattern
|
||||
* This is responsible for join pattern such as project on join
|
||||
* */
|
||||
public class MaterializedViewProjectJoinRule extends AbstractMaterializedViewJoinRule implements RewriteRuleFactory {
|
||||
public class MaterializedViewProjectJoinRule extends AbstractMaterializedViewJoinRule {
|
||||
|
||||
public static final MaterializedViewProjectJoinRule INSTANCE = new MaterializedViewProjectJoinRule();
|
||||
|
||||
@ -42,6 +40,6 @@ public class MaterializedViewProjectJoinRule extends AbstractMaterializedViewJoi
|
||||
logicalProject(logicalJoin(any(), any())).thenApplyMulti(ctx -> {
|
||||
LogicalProject<LogicalJoin<Plan, Plan>> root = ctx.root;
|
||||
return rewrite(root, ctx.cascadesContext);
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_PROJECT_JOIN, RulePromise.EXPLORE));
|
||||
}).toRule(RuleType.MATERIALIZED_VIEW_PROJECT_JOIN));
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,14 +18,13 @@
|
||||
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 {
|
||||
public class MaterializedViewScanRule extends AbstractMaterializedViewRule {
|
||||
|
||||
@Override
|
||||
public List<Rule> buildRules() {
|
||||
|
||||
@ -282,7 +282,9 @@ public class StructInfo {
|
||||
@Override
|
||||
public Void visit(Plan plan, Set<Expression> predicates) {
|
||||
// Just collect the filter in top plan, if meet other node except project and filter, return
|
||||
if (!(plan instanceof LogicalProject) && !(plan instanceof LogicalFilter)) {
|
||||
if (!(plan instanceof LogicalProject)
|
||||
&& !(plan instanceof LogicalFilter)
|
||||
&& !(plan instanceof LogicalAggregate)) {
|
||||
return null;
|
||||
}
|
||||
if (plan instanceof LogicalFilter) {
|
||||
@ -396,7 +398,7 @@ public class StructInfo {
|
||||
super.visit(aggregate, context);
|
||||
return true;
|
||||
}
|
||||
if (plan instanceof LogicalProject) {
|
||||
if (plan instanceof LogicalProject || plan instanceof LogicalFilter) {
|
||||
super.visit(plan, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
// 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.trees.TreeNode;
|
||||
import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This represents any expression, it means it equals any expression
|
||||
*/
|
||||
public class Any extends Expression implements LeafExpression {
|
||||
|
||||
public static final Any INSTANCE = new Any(ImmutableList.of());
|
||||
|
||||
private Any(Expression... children) {
|
||||
super(children);
|
||||
}
|
||||
|
||||
private Any(List<Expression> children) {
|
||||
super(children);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitAny(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deepEquals(TreeNode<?> that) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -77,10 +77,6 @@ public abstract class AggregateFunction extends BoundFunction implements Expects
|
||||
return distinct;
|
||||
}
|
||||
|
||||
public Class<? extends AggregateFunction> getRollup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
// 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.functions.agg;
|
||||
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Function;
|
||||
|
||||
/**
|
||||
* Could roll up trait, if a function could roll up in aggregate, it will implement the interface
|
||||
*/
|
||||
public interface CouldRollUp {
|
||||
|
||||
/**
|
||||
* construct the roll up function with custom param
|
||||
*/
|
||||
Function constructRollUp(Expression param, Expression... varParams);
|
||||
}
|
||||
@ -22,6 +22,7 @@ import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Function;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.window.SupportWindowAnalytic;
|
||||
import org.apache.doris.nereids.trees.expressions.literal.Literal;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
|
||||
@ -36,7 +37,7 @@ import java.util.List;
|
||||
|
||||
/** count agg function. */
|
||||
public class Count extends AggregateFunction
|
||||
implements ExplicitlyCastableSignature, AlwaysNotNullable, SupportWindowAnalytic {
|
||||
implements ExplicitlyCastableSignature, AlwaysNotNullable, SupportWindowAnalytic, CouldRollUp {
|
||||
|
||||
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
|
||||
// count(*)
|
||||
@ -142,4 +143,13 @@ public class Count extends AggregateFunction
|
||||
public List<FunctionSignature> getSignatures() {
|
||||
return SIGNATURES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function constructRollUp(Expression param, Expression... varParams) {
|
||||
if (this.isDistinct()) {
|
||||
return new BitmapUnionCount(param);
|
||||
} else {
|
||||
return new Sum(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import org.apache.doris.catalog.Type;
|
||||
import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Function;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.window.SupportWindowAnalytic;
|
||||
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
|
||||
@ -34,7 +35,7 @@ import java.util.List;
|
||||
|
||||
/** max agg function. */
|
||||
public class Max extends NullableAggregateFunction
|
||||
implements UnaryExpression, CustomSignature, SupportWindowAnalytic {
|
||||
implements UnaryExpression, CustomSignature, SupportWindowAnalytic, CouldRollUp {
|
||||
public Max(Expression child) {
|
||||
this(false, false, child);
|
||||
}
|
||||
@ -80,4 +81,9 @@ public class Max extends NullableAggregateFunction
|
||||
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitMax(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function constructRollUp(Expression param, Expression... varParams) {
|
||||
return new Max(this.distinct, this.alwaysNullable, param);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import org.apache.doris.catalog.Type;
|
||||
import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Function;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.window.SupportWindowAnalytic;
|
||||
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
|
||||
@ -34,7 +35,7 @@ import java.util.List;
|
||||
|
||||
/** min agg function. */
|
||||
public class Min extends NullableAggregateFunction
|
||||
implements UnaryExpression, CustomSignature, SupportWindowAnalytic {
|
||||
implements UnaryExpression, CustomSignature, SupportWindowAnalytic, CouldRollUp {
|
||||
|
||||
public Min(Expression child) {
|
||||
this(false, false, child);
|
||||
@ -81,4 +82,9 @@ public class Min extends NullableAggregateFunction
|
||||
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitMin(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function constructRollUp(Expression param, Expression... varParams) {
|
||||
return new Min(this.distinct, this.alwaysNullable, param);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import org.apache.doris.nereids.exceptions.AnalysisException;
|
||||
import org.apache.doris.nereids.trees.expressions.Expression;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.ComputePrecisionForSum;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.Function;
|
||||
import org.apache.doris.nereids.trees.expressions.functions.window.SupportWindowAnalytic;
|
||||
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
|
||||
@ -45,7 +46,8 @@ import java.util.List;
|
||||
* AggregateFunction 'sum'. This class is generated by GenerateFunction.
|
||||
*/
|
||||
public class Sum extends NullableAggregateFunction
|
||||
implements UnaryExpression, ExplicitlyCastableSignature, ComputePrecisionForSum, SupportWindowAnalytic {
|
||||
implements UnaryExpression, ExplicitlyCastableSignature, ComputePrecisionForSum, SupportWindowAnalytic,
|
||||
CouldRollUp {
|
||||
|
||||
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
|
||||
FunctionSignature.ret(BigIntType.INSTANCE).args(BooleanType.INSTANCE),
|
||||
@ -111,7 +113,7 @@ public class Sum extends NullableAggregateFunction
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends AggregateFunction> getRollup() {
|
||||
return Sum.class;
|
||||
public Function constructRollUp(Expression param, Expression... varParams) {
|
||||
return new Sum(this.distinct, param);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ import org.apache.doris.nereids.trees.expressions.Add;
|
||||
import org.apache.doris.nereids.trees.expressions.AggregateExpression;
|
||||
import org.apache.doris.nereids.trees.expressions.Alias;
|
||||
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.AssertNumRowsElement;
|
||||
import org.apache.doris.nereids.trees.expressions.BinaryArithmetic;
|
||||
@ -499,6 +500,10 @@ public abstract class ExpressionVisitor<R, C>
|
||||
return visitMatch(matchPhrasePrefix, context);
|
||||
}
|
||||
|
||||
public R visitAny(Any any, C context) {
|
||||
return visit(any, context);
|
||||
}
|
||||
|
||||
/* ********************************************************************************************
|
||||
* Unbound expressions
|
||||
* ********************************************************************************************/
|
||||
|
||||
@ -28,6 +28,7 @@ import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisit
|
||||
import org.apache.doris.nereids.trees.plans.Plan;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer.ExpressionReplaceContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -66,9 +67,28 @@ public class ExpressionLineageReplacer extends DefaultPlanVisitor<Expression, Ex
|
||||
public Expression visitNamedExpression(NamedExpression namedExpression,
|
||||
Map<ExprId, Expression> exprIdExpressionMap) {
|
||||
if (exprIdExpressionMap.containsKey(namedExpression.getExprId())) {
|
||||
return super.visit(exprIdExpressionMap.get(namedExpression.getExprId()), exprIdExpressionMap);
|
||||
return visit(exprIdExpressionMap.get(namedExpression.getExprId()), exprIdExpressionMap);
|
||||
}
|
||||
return super.visitNamedExpression(namedExpression, exprIdExpressionMap);
|
||||
return visit(namedExpression, exprIdExpressionMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression visit(Expression expr, Map<ExprId, Expression> exprIdExpressionMap) {
|
||||
if (expr instanceof NamedExpression
|
||||
&& expr.arity() == 0
|
||||
&& exprIdExpressionMap.containsKey(((NamedExpression) expr).getExprId())) {
|
||||
expr = exprIdExpressionMap.get(((NamedExpression) expr).getExprId());
|
||||
}
|
||||
List<Expression> newChildren = new ArrayList<>(expr.arity());
|
||||
boolean hasNewChildren = false;
|
||||
for (Expression child : expr.children()) {
|
||||
Expression newChild = child.accept(this, exprIdExpressionMap);
|
||||
if (newChild != child) {
|
||||
hasNewChildren = true;
|
||||
}
|
||||
newChildren.add(newChild);
|
||||
}
|
||||
return hasNewChildren ? expr.withChildren(newChildren) : expr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -362,12 +362,7 @@ public class ExpressionUtils {
|
||||
@Override
|
||||
public Expression visit(Expression expr, Map<? extends Expression, ? extends Expression> replaceMap) {
|
||||
if (replaceMap.containsKey(expr)) {
|
||||
Expression replacedExpression = replaceMap.get(expr);
|
||||
if (replacedExpression instanceof SlotReference
|
||||
&& replacedExpression.nullable() != expr.nullable()) {
|
||||
replacedExpression = ((SlotReference) replacedExpression).withNullable(expr.nullable());
|
||||
}
|
||||
return replacedExpression;
|
||||
return replaceMap.get(expr);
|
||||
}
|
||||
return super.visit(expr, replaceMap);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user