[fix](nereids) Fix data wrong using mv rewrite and ignore case when getting mv related partition table (#28699)

1. Fix data wrong using mv rewrite
2. Ignore case when getting mv related partition table
3. Enable infer expression column name without alias when create mv
This commit is contained in:
seawinde
2023-12-20 17:59:46 +08:00
committed by GitHub
parent 2b2d3d0eb1
commit 9a5ec43f05
21 changed files with 310 additions and 174 deletions

View File

@ -35,7 +35,6 @@ 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;
@ -199,13 +198,12 @@ public class MTMV extends OlapTable {
return Sets.newHashSet(split);
}
// this should use the same connectContext with query, to use the same session variable
public MTMVCache getOrGenerateCache(ConnectContext parent) throws AnalysisException {
public MTMVCache getOrGenerateCache() throws AnalysisException {
if (cache == null) {
writeMvLock();
try {
if (cache == null) {
this.cache = MTMVCache.from(this, parent);
this.cache = MTMVCache.from(this, MTMVPlanUtil.createMTMVContext(this));
}
} finally {
writeMvUnlock();

View File

@ -63,7 +63,8 @@ public class MTMVCache {
public static MTMVCache from(MTMV mtmv, ConnectContext connectContext) {
LogicalPlan unboundMvPlan = new NereidsParser().parseSingle(mtmv.getQuerySql());
// TODO: connect context set current db when create mv by use database
// this will be removed in the future when support join derivation
connectContext.getSessionVariable().setDisableNereidsRules("INFER_PREDICATES, ELIMINATE_OUTER_JOIN");
StatementContext mvSqlStatementContext = new StatementContext(connectContext,
new OriginStatement(mtmv.getQuerySql(), 0));
NereidsPlanner planner = new NereidsPlanner(mvSqlStatementContext);

View File

@ -93,6 +93,22 @@ public class PatternDescriptor<INPUT_TYPE extends Plan> {
return new PatternMatcher<>(pattern, defaultPromise, matchedAction);
}
/**
* Apply rule to return multi result, catch exception to make sure no influence on other rule
*/
public <OUTPUT_TYPE extends Plan> PatternMatcher<INPUT_TYPE, OUTPUT_TYPE> thenApplyMultiNoThrow(
MatchedMultiAction<INPUT_TYPE, OUTPUT_TYPE> matchedMultiAction) {
MatchedMultiAction<INPUT_TYPE, OUTPUT_TYPE> adaptMatchedMultiAction = ctx -> {
try {
return matchedMultiAction.apply(ctx);
} catch (Exception ex) {
LOG.warn("nereids apply rule failed, because {}", ex.getMessage(), ex);
return null;
}
};
return new PatternMatcher<>(pattern, defaultPromise, adaptMatchedMultiAction);
}
public Pattern<INPUT_TYPE> getPattern() {
return pattern;
}

View File

@ -84,7 +84,7 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
List<MaterializationContext> materializationContexts = cascadesContext.getMaterializationContexts();
List<Plan> rewriteResults = new ArrayList<>();
if (materializationContexts.isEmpty()) {
logger.info(currentClassName + " materializationContexts is empty so return");
logger.debug(currentClassName + " materializationContexts is empty so return");
return rewriteResults;
}
@ -92,7 +92,7 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
// 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");
logger.debug(currentClassName + " queryStructInfo is not valid so return");
return rewriteResults;
}
@ -101,42 +101,42 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
if (queryPlan.getGroupExpression().isPresent()
&& materializationContext.alreadyRewrite(
queryPlan.getGroupExpression().get().getOwnerGroup().getGroupId())) {
logger.info(currentClassName + " this group is already rewritten so skip");
logger.debug(currentClassName + " this group is already rewritten so skip");
continue;
}
MTMV mtmv = materializationContext.getMTMV();
MTMVCache mtmvCache = getCacheFromMTMV(mtmv, cascadesContext);
MTMVCache mtmvCache = getCacheFromMTMV(mtmv);
if (mtmvCache == null) {
logger.info(currentClassName + " mv cache is null so return");
logger.warn(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");
logger.warn(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");
logger.debug(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");
logger.debug(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");
logger.warn(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");
logger.warn(currentClassName + " query to view slot mapping null so continue");
continue;
}
LogicalCompatibilityContext compatibilityContext =
@ -145,7 +145,7 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
List<Expression> pulledUpExpressions = StructInfo.isGraphLogicalEquals(queryStructInfo, viewStructInfo,
compatibilityContext);
if (pulledUpExpressions == null) {
logger.info(currentClassName + " graph logical is not equals so continue");
logger.debug(currentClassName + " graph logical is not equals so continue");
continue;
}
// set pulled up expression to queryStructInfo predicates and update related predicates
@ -156,13 +156,13 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
queryToViewSlotMapping);
// Can not compensate, bail out
if (compensatePredicates.isEmpty()) {
logger.info(currentClassName + " predicate compensate fail so continue");
logger.debug(currentClassName + " predicate compensate fail so continue");
continue;
}
Plan rewritedPlan;
Plan rewrittenPlan;
Plan mvScan = materializationContext.getMvScanPlan();
if (compensatePredicates.isAlwaysTrue()) {
rewritedPlan = mvScan;
rewrittenPlan = mvScan;
} else {
// Try to rewrite compensate predicates by using mv scan
List<Expression> rewriteCompensatePredicates = rewriteExpression(
@ -172,39 +172,52 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
queryToViewSlotMapping,
true);
if (rewriteCompensatePredicates.isEmpty()) {
logger.info(currentClassName + " compensate predicate rewrite by view fail so continue");
logger.debug(currentClassName + " compensate predicate rewrite by view fail so continue");
continue;
}
rewritedPlan = new LogicalFilter<>(Sets.newHashSet(rewriteCompensatePredicates), mvScan);
rewrittenPlan = new LogicalFilter<>(Sets.newHashSet(rewriteCompensatePredicates), mvScan);
}
// Rewrite query by view
rewritedPlan = rewriteQueryByView(matchMode,
rewrittenPlan = rewriteQueryByView(matchMode,
queryStructInfo,
viewStructInfo,
queryToViewSlotMapping,
rewritedPlan,
rewrittenPlan,
materializationContext);
if (rewritedPlan == null) {
logger.info(currentClassName + " rewrite query by view fail so continue");
if (rewrittenPlan == null) {
logger.debug(currentClassName + " rewrite query by view fail so continue");
continue;
}
if (!checkPartitionIsValid(queryStructInfo, materializationContext, cascadesContext)) {
logger.info(currentClassName + " check partition validation fail so continue");
logger.debug(currentClassName + " check partition validation fail so continue");
continue;
}
if (!checkOutput(queryPlan, rewrittenPlan)) {
logger.debug(currentClassName + " check output validation fail so continue");
continue;
}
// run rbo job on mv rewritten plan
CascadesContext rewrittenPlanContext =
CascadesContext.initContext(cascadesContext.getStatementContext(), rewritedPlan,
CascadesContext.initContext(cascadesContext.getStatementContext(), rewrittenPlan,
cascadesContext.getCurrentJobContext().getRequiredProperties());
Rewriter.getWholeTreeRewriter(cascadesContext).execute();
rewritedPlan = rewrittenPlanContext.getRewritePlan();
logger.info(currentClassName + "rewrite by materialized view success");
rewriteResults.add(rewritedPlan);
rewrittenPlan = rewrittenPlanContext.getRewritePlan();
logger.debug(currentClassName + "rewrite by materialized view success");
rewriteResults.add(rewrittenPlan);
}
}
return rewriteResults;
}
protected boolean checkOutput(Plan sourcePlan, Plan rewrittenPlan) {
if (sourcePlan.getGroupExpression().isPresent() && !rewrittenPlan.getLogicalProperties().equals(
sourcePlan.getGroupExpression().get().getOwnerGroup().getLogicalProperties())) {
logger.error("rewrittenPlan output logical properties is not same with target group");
return false;
}
return true;
}
/**
* Partition will be pruned in query then add the record the partitions to select partitions on
* catalog relation.
@ -276,10 +289,10 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac
&& relatedTalbeValidSet.containsAll(relatedTableSelectedPartitionToCheck);
}
private MTMVCache getCacheFromMTMV(MTMV mtmv, CascadesContext cascadesContext) {
private MTMVCache getCacheFromMTMV(MTMV mtmv) {
MTMVCache cache;
try {
cache = mtmv.getOrGenerateCache(cascadesContext.getConnectContext());
cache = mtmv.getOrGenerateCache();
} catch (AnalysisException analysisException) {
logger.warn(this.getClass().getSimpleName() + " get mtmv cache analysisException", analysisException);
return null;

View File

@ -67,7 +67,7 @@ public class MaterializationContext {
MTMVCache mtmvCache = null;
try {
mtmvCache = mtmv.getOrGenerateCache(cascadesContext.getConnectContext());
mtmvCache = mtmv.getOrGenerateCache();
} catch (AnalysisException e) {
LOG.warn("MaterializationContext init mv cache generate fail", e);
}

View File

@ -36,7 +36,7 @@ public class MaterializedViewAggregateRule extends AbstractMaterializedViewAggre
@Override
public List<Rule> buildRules() {
return ImmutableList.of(
logicalAggregate(any()).thenApplyMulti(ctx -> {
logicalAggregate(any()).thenApplyMultiNoThrow(ctx -> {
LogicalAggregate<Plan> root = ctx.root;
return rewrite(root, ctx.cascadesContext);
}).toRule(RuleType.MATERIALIZED_VIEW_ONLY_AGGREGATE));

View File

@ -35,7 +35,7 @@ public class MaterializedViewProjectAggregateRule extends AbstractMaterializedVi
@Override
public List<Rule> buildRules() {
return ImmutableList.of(
logicalProject(logicalAggregate(any())).thenApplyMulti(ctx -> {
logicalProject(logicalAggregate(any())).thenApplyMultiNoThrow(ctx -> {
LogicalProject<LogicalAggregate<Plan>> root = ctx.root;
return rewrite(root, ctx.cascadesContext);
}).toRule(RuleType.MATERIALIZED_VIEW_PROJECT_AGGREGATE));

View File

@ -37,7 +37,7 @@ public class MaterializedViewProjectJoinRule extends AbstractMaterializedViewJoi
@Override
public List<Rule> buildRules() {
return ImmutableList.of(
logicalProject(logicalJoin(any(), any())).thenApplyMulti(ctx -> {
logicalProject(logicalJoin(any(), any())).thenApplyMultiNoThrow(ctx -> {
LogicalProject<LogicalJoin<Plan, Plan>> root = ctx.root;
return rewrite(root, ctx.cascadesContext);
}).toRule(RuleType.MATERIALIZED_VIEW_PROJECT_JOIN));

View File

@ -63,7 +63,7 @@ public class MaterializedViewUtils {
Slot columnExpr = null;
// get column slot
for (Slot outputSlot : outputExpressions) {
if (outputSlot.getName().equals(column)) {
if (outputSlot.getName().equalsIgnoreCase(column)) {
columnExpr = outputSlot;
break;
}

View File

@ -23,9 +23,10 @@ import org.apache.doris.nereids.util.ExpressionUtils;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -34,7 +35,7 @@ import java.util.stream.Collectors;
public class Predicates {
// Predicates that can be pulled up
private final List<Expression> pulledUpPredicates = new ArrayList<>();
private final Set<Expression> pulledUpPredicates = new HashSet<>();
private Predicates() {
}
@ -49,7 +50,7 @@ public class Predicates {
return predicates;
}
public List<? extends Expression> getPulledUpPredicates() {
public Set<? extends Expression> getPulledUpPredicates() {
return pulledUpPredicates;
}

View File

@ -20,17 +20,16 @@ package org.apache.doris.nereids.rules.exploration.mv;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
import org.apache.doris.nereids.trees.expressions.EqualPredicate;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Or;
import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
import org.apache.doris.nereids.util.ExpressionUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Split the expression to equal, range and residual predicate.
@ -39,27 +38,26 @@ import java.util.List;
*/
public class PredicatesSplitter {
private final List<Expression> equalPredicates = new ArrayList<>();
private final List<Expression> rangePredicates = new ArrayList<>();
private final List<Expression> residualPredicates = new ArrayList<>();
private final Set<Expression> equalPredicates = new HashSet<>();
private final Set<Expression> rangePredicates = new HashSet<>();
private final Set<Expression> residualPredicates = new HashSet<>();
private final List<Expression> conjunctExpressions;
private final PredicateExtract instance = new PredicateExtract();
public PredicatesSplitter(Expression target) {
this.conjunctExpressions = ExpressionUtils.extractConjunction(target);
PredicateExtract instance = new PredicateExtract();
for (Expression expression : conjunctExpressions) {
expression.accept(instance, expression);
expression.accept(instance, null);
}
}
/**
* PredicateExtract
* extract to equal, range, residual predicate set
*/
public class PredicateExtract extends DefaultExpressionVisitor<Void, Expression> {
public class PredicateExtract extends DefaultExpressionVisitor<Void, Void> {
@Override
public Void visitComparisonPredicate(ComparisonPredicate comparisonPredicate, Expression sourceExpression) {
public Void visitComparisonPredicate(ComparisonPredicate comparisonPredicate, Void context) {
Expression leftArg = comparisonPredicate.getArgument(0);
Expression rightArg = comparisonPredicate.getArgument(1);
boolean leftArgOnlyContainsColumnRef = containOnlyColumnRef(leftArg, true);
@ -81,12 +79,9 @@ public class PredicatesSplitter {
}
@Override
public Void visitCompoundPredicate(CompoundPredicate compoundPredicate, Expression context) {
if (compoundPredicate instanceof Or) {
residualPredicates.add(compoundPredicate);
return null;
}
return super.visitCompoundPredicate(compoundPredicate, context);
public Void visit(Expression expr, Void context) {
residualPredicates.add(expr);
return null;
}
}
@ -98,7 +93,7 @@ public class PredicatesSplitter {
}
private static boolean containOnlyColumnRef(Expression expression, boolean allowCast) {
if (expression instanceof SlotReference && ((SlotReference) expression).isColumnFromTable()) {
if (expression instanceof SlotReference && expression.isColumnFromTable()) {
return true;
}
if (allowCast && expression instanceof Cast) {

View File

@ -162,7 +162,7 @@ public class StructInfo {
private void predicatesDerive() {
// construct equivalenceClass according to equals predicates
List<Expression> shuttledExpression = ExpressionUtils.shuttleExpressionWithLineage(
this.predicates.getPulledUpPredicates(), originalPlan).stream()
new ArrayList<>(this.predicates.getPulledUpPredicates()), originalPlan).stream()
.map(Expression.class::cast)
.collect(Collectors.toList());
SplitPredicate splitPredicate = Predicates.splitPredicates(ExpressionUtils.and(shuttledExpression));

View File

@ -43,6 +43,7 @@ import org.apache.doris.mtmv.MTMVRelation;
import org.apache.doris.mtmv.MTMVUtil;
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.nereids.NereidsPlanner;
import org.apache.doris.nereids.analyzer.UnboundResultSink;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.properties.PhysicalProperties;
import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewUtils;
@ -54,6 +55,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
import org.apache.doris.nereids.trees.plans.logical.LogicalSink;
import org.apache.doris.nereids.trees.plans.visitor.NondeterministicFunctionCollector;
import org.apache.doris.nereids.trees.plans.visitor.TableCollector;
import org.apache.doris.nereids.trees.plans.visitor.TableCollector.TableCollectorContext;
@ -199,7 +201,9 @@ public class CreateMTMVInfo {
public void analyzeQuery(ConnectContext ctx) {
// create table as select
NereidsPlanner planner = new NereidsPlanner(ctx.getStatementContext());
Plan plan = planner.plan(logicalQuery, PhysicalProperties.ANY, ExplainLevel.ALL_PLAN);
// this is for expression column name infer when not use alias
LogicalSink<Plan> logicalSink = new UnboundResultSink<>(logicalQuery);
Plan plan = planner.plan(logicalSink, PhysicalProperties.ANY, ExplainLevel.ALL_PLAN);
if (plan.anyMatch(node -> node instanceof OneRowRelation)) {
throw new AnalysisException("at least contain one table");
}

View File

@ -113,7 +113,7 @@ public class MaterializedViewUtilsTest extends TestWithFeService {
nereidsPlanner -> {
Plan rewrittenPlan = nereidsPlanner.getRewrittenPlan();
Optional<RelatedTableInfo> relatedTableInfo =
MaterializedViewUtils.getRelatedTableInfo("L_SHIPDATE", rewrittenPlan);
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
checkRelatedTableInfo(relatedTableInfo,
"lineitem",
"L_SHIPDATE",
@ -142,7 +142,7 @@ public class MaterializedViewUtilsTest extends TestWithFeService {
MaterializedViewUtils.getRelatedTableInfo("ship_data_alias", rewrittenPlan);
checkRelatedTableInfo(relatedTableInfo,
"lineitem",
"L_SHIPDATE",
"l_shipdate",
true);
});
}
@ -169,7 +169,7 @@ public class MaterializedViewUtilsTest extends TestWithFeService {
nereidsPlanner -> {
Plan rewrittenPlan = nereidsPlanner.getRewrittenPlan();
Optional<RelatedTableInfo> relatedTableInfo =
MaterializedViewUtils.getRelatedTableInfo("L_SHIPDATE", rewrittenPlan);
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
checkRelatedTableInfo(relatedTableInfo,
"lineitem",
"L_SHIPDATE",
@ -177,6 +177,36 @@ public class MaterializedViewUtilsTest extends TestWithFeService {
});
}
@Test
public void getRelatedTableInfoUseRightTest() {
PlanChecker.from(connectContext)
.checkExplain("SELECT t1.L_SHIPDATE, t2.O_ORDERDATE, t1.L_QUANTITY, t2.O_ORDERSTATUS, "
+ "count(distinct case when t1.L_SUPPKEY > 0 then t2.O_ORDERSTATUS else null end) as cnt_1 "
+ "from "
+ " (select * from "
+ " lineitem "
+ " where L_SHIPDATE in ('2017-01-30')) t1 "
+ "left join "
+ " (select * from "
+ " orders "
+ " where O_ORDERDATE in ('2017-01-30')) t2 "
+ "on t1.L_ORDERKEY = t2.O_ORDERKEY "
+ "group by "
+ "t1.L_SHIPDATE, "
+ "t2.O_ORDERDATE, "
+ "t1.L_QUANTITY, "
+ "t2.O_ORDERSTATUS;",
nereidsPlanner -> {
Plan rewrittenPlan = nereidsPlanner.getRewrittenPlan();
Optional<RelatedTableInfo> relatedTableInfo =
MaterializedViewUtils.getRelatedTableInfo("o_orderdate", rewrittenPlan);
checkRelatedTableInfo(relatedTableInfo,
"orders",
"O_ORDERDATE",
true);
});
}
@Test
public void getRelatedTableInfoTestWithoutPartitionTest() {
PlanChecker.from(connectContext)
@ -212,7 +242,7 @@ public class MaterializedViewUtilsTest extends TestWithFeService {
nereidsPlanner -> {
Plan rewrittenPlan = nereidsPlanner.getRewrittenPlan();
Optional<RelatedTableInfo> relatedTableInfo =
MaterializedViewUtils.getRelatedTableInfo("L_SHIPDATE", rewrittenPlan);
MaterializedViewUtils.getRelatedTableInfo("l_shipdate", rewrittenPlan);
checkRelatedTableInfo(relatedTableInfo,
"lineitem",
"L_SHIPDATE",
@ -298,7 +328,7 @@ public class MaterializedViewUtilsTest extends TestWithFeService {
} catch (Exception exception) {
Assertions.fail();
}
Assertions.assertEquals(relatedTableInfo.get().getColumn(), expectColumnName);
Assertions.assertEquals(relatedTableInfo.get().getColumn().toLowerCase(), expectColumnName.toLowerCase());
Assertions.assertTrue(pctPossible);
}
}

View File

@ -48,7 +48,7 @@ public class PredicatesSplitterTest extends ExpressionRewriteTestHelper {
"c = d or a = 10");
assetEquals("a = b and c + d = e and a > 7 and 10 > d",
"a = b",
"a > 7 and 10 > d",
"10 > d and a > 7",
"c + d = e");
assetEquals("a = b and c + d = e or a > 7 and 10 > d",
"",

View File

@ -121,3 +121,17 @@
3 3 2023-12-11 43.20 43.20 43.20 1
4 3 2023-12-09 11.50 11.50 11.50 1
-- !query1_1_before --
1 yy 0 0 11.50 11.50 11.50 1
-- !query1_1_after --
1 yy 0 0 11.50 11.50 11.50 1
-- !query2_0_before --
2 mi 0 0 57.40 56.20 1.20 2
2 mm 0 0 43.20 43.20 43.20 1
-- !query2_0_after --
2 mi 0 0 57.40 56.20 1.20 2
2 mm 0 0 43.20 43.20 43.20 1

View File

@ -2,9 +2,20 @@
-- !query1_0_before --
-- !query1_0_after --
1 yy 0 0 77.50 33.50 9.50 5
2 mi 0 0 57.40 56.20 1.20 2
2 mm 0 0 43.20 43.20 43.20 1
-- !query1_1_before --
2023-12-08 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-09 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-10 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-11 2 mm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-12 2 mi 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-- !query1_1_after --
2023-12-08 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-09 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-10 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-11 2 mm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2023-12-12 2 mi 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-- !query1_2_before --
1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
@ -67,7 +78,6 @@
2 3 2023-12-12 57.40 56.20 1.20 2 0
2 4 2023-12-10 46.00 33.50 12.50 2 0
3 3 2023-12-11 43.20 43.20 43.20 1 0
4 3 2023-12-09 11.50 11.50 11.50 1 0
-- !query16_0_before --
2 3 2023-12-08 20.00 10.50 9.50 2 0
@ -89,6 +99,20 @@
-- !query17_0_after --
3 3 2023-12-11 43.20 43.20 43.20 1 0
-- !query17_1_before --
1 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0
2 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0
3 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0
4 2 mm 0 0 0 0 0 0 0 0 0 0 0 0 0
5 2 mi 0 0 0 0 0 0 0 0 0 0 0 0 0
-- !query17_1_after --
1 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0
2 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0
3 1 yy 0 0 0 0 0 0 0 0 0 0 0 0 0
4 2 mm 0 0 0 0 0 0 0 0 0 0 0 0 0
5 2 mi 0 0 0 0 0 0 0 0 0 0 0 0 0
-- !query18_0_before --
-- !query18_0_after --
@ -125,3 +149,9 @@
4 2 43.20
6 2 57.40
-- !query20_0_before --
0 0 0 0 0 0 0 0 0 0 0 0
-- !query20_0_after --
0 0 0 0 0 0 0 0 0 0 0 0

View File

@ -238,8 +238,6 @@
4
4
4
6
6
-- !query5_0_before --
4
@ -261,6 +259,18 @@
6
6
-- !query6_0_before --
2 3 2023-12-08
2 3 2023-12-08
-- !query6_0_after --
2 3 2023-12-08
2 3 2023-12-08
-- !query7_0_before --
-- !query7_0_after --
-- !query10_0_before --
-- !query10_0_after --

View File

@ -144,6 +144,30 @@ suite("aggregate_with_roll_up") {
}
}
def check_rewrite_with_force_analyze = { mv_sql, query_sql, mv_name ->
sql """DROP MATERIALIZED VIEW IF EXISTS ${mv_name}"""
sql"""
CREATE MATERIALIZED VIEW ${mv_name}
BUILD IMMEDIATE REFRESH COMPLETE ON MANUAL
DISTRIBUTED BY RANDOM BUCKETS 2
PROPERTIES ('replication_num' = '1')
AS ${mv_sql}
"""
sql "analyze table ${mv_name} with sync;"
sql "analyze table lineitem with sync;"
sql "analyze table orders with sync;"
sql "analyze table partsupp with sync;"
def job_name = getJobName(db, mv_name);
waitingMTMVTaskFinished(job_name)
explain {
sql("${query_sql}")
contains "(${mv_name})"
}
}
def check_not_match = { mv_sql, query_sql, mv_name ->
sql """DROP MATERIALIZED VIEW IF EXISTS ${mv_name}"""
@ -163,71 +187,6 @@ suite("aggregate_with_roll_up") {
}
}
// single table
// filter + use roll up dimension
def mv1_1 = "select o_orderdate, o_shippriority, o_comment, " +
"sum(o_totalprice) as sum_total, " +
"max(o_totalprice) as max_total, " +
"min(o_totalprice) as min_total, " +
"count(*) as count_all, " +
"bitmap_union(to_bitmap(case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end)) cnt_1, " +
"bitmap_union(to_bitmap(case when o_shippriority > 2 and o_orderkey IN (2) then o_custkey else null end)) as cnt_2 " +
"from orders " +
"group by " +
"o_orderdate, " +
"o_shippriority, " +
"o_comment "
def query1_1 = "select o_shippriority, o_comment, " +
"count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end) as cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and o_orderkey IN (2) then o_custkey else null end) as cnt_2, " +
"sum(o_totalprice), " +
"max(o_totalprice), " +
"min(o_totalprice), " +
"count(*) " +
"from orders " +
"where o_orderdate = '2023-12-09' " +
"group by " +
"o_shippriority, " +
"o_comment "
// rewrite success but cbo not chose, tmp
// order_qt_query1_1_before "${query1_1}"
// check_rewrite(mv1_1, query1_1, "mv1_1")
// order_qt_query1_1_after "${query1_1}"
// sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_1"""
// filter + not use roll up dimension
def mv2_0 = "select o_orderdate, o_shippriority, o_comment, " +
"sum(o_totalprice) as sum_total, " +
"max(o_totalprice) as max_total, " +
"min(o_totalprice) as min_total, " +
"count(*) as count_all, " +
"bitmap_union(to_bitmap(case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end)) cnt_1, " +
"bitmap_union(to_bitmap(case when o_shippriority > 2 and o_orderkey IN (2) then o_custkey else null end)) as cnt_2 " +
"from orders " +
"group by " +
"o_orderdate, " +
"o_shippriority, " +
"o_comment "
def query2_0 = "select o_shippriority, o_comment, " +
"count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end) as cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and o_orderkey IN (2) then o_custkey else null end) as cnt_2, " +
"sum(o_totalprice), " +
"max(o_totalprice), " +
"min(o_totalprice), " +
"count(*) " +
"from orders " +
"where o_shippriority = 2 " +
"group by " +
"o_shippriority, " +
"o_comment "
// rewrite success but cbo not chose, tmp
// order_qt_query2_0_before "${query2_0}"
// check_rewrite(mv2_0, query2_0, "mv2_0")
// order_qt_query2_0_after "${query2_0}"
// sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_0"""
// multi table
// filter inside + left + use roll up dimension
def mv13_0 = "select l_shipdate, o_orderdate, l_partkey, l_suppkey, " +
@ -710,5 +669,72 @@ suite("aggregate_with_roll_up") {
order_qt_query25_0_after "${query25_0}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv25_0"""
// single table
// filter + use roll up dimension
def mv1_1 = "select o_orderdate, o_shippriority, o_comment, " +
"sum(o_totalprice) as sum_total, " +
"max(o_totalprice) as max_total, " +
"min(o_totalprice) as min_total, " +
"count(*) as count_all, " +
"bitmap_union(to_bitmap(case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end)) cnt_1, " +
"bitmap_union(to_bitmap(case when o_shippriority > 2 and o_orderkey IN (2) then o_custkey else null end)) as cnt_2 " +
"from orders " +
"group by " +
"o_orderdate, " +
"o_shippriority, " +
"o_comment "
def query1_1 = "select o_shippriority, o_comment, " +
"count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end) as cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and o_orderkey IN (2) then o_custkey else null end) as cnt_2, " +
"sum(o_totalprice), " +
"max(o_totalprice), " +
"min(o_totalprice), " +
"count(*) " +
"from orders " +
"where o_orderdate = '2023-12-09' " +
"group by " +
"o_shippriority, " +
"o_comment "
order_qt_query1_1_before "${query1_1}"
// rewrite success, for cbo chose, should force analyze
// because data volume is small and mv plan is almost same to query plan
check_rewrite_with_force_analyze(mv1_1, query1_1, "mv1_1")
order_qt_query1_1_after "${query1_1}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_1"""
// filter + not use roll up dimension
def mv2_0 = "select o_orderdate, o_shippriority, o_comment, " +
"sum(o_totalprice) as sum_total, " +
"max(o_totalprice) as max_total, " +
"min(o_totalprice) as min_total, " +
"count(*) as count_all, " +
"bitmap_union(to_bitmap(case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end)) cnt_1, " +
"bitmap_union(to_bitmap(case when o_shippriority > 2 and o_orderkey IN (2) then o_custkey else null end)) as cnt_2 " +
"from orders " +
"group by " +
"o_orderdate, " +
"o_shippriority, " +
"o_comment "
def query2_0 = "select o_shippriority, o_comment, " +
"count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end) as cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and o_orderkey IN (2) then o_custkey else null end) as cnt_2, " +
"sum(o_totalprice), " +
"max(o_totalprice), " +
"min(o_totalprice), " +
"count(*) " +
"from orders " +
"where o_shippriority = 2 " +
"group by " +
"o_shippriority, " +
"o_comment "
order_qt_query2_0_before "${query2_0}"
// rewrite success, for cbo chose, should force analyze
// because data volume is small and mv plan is almost same to query plan
check_rewrite_with_force_analyze(mv2_0, query2_0, "mv2_0")
order_qt_query2_0_after "${query2_0}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_0"""
// can not rewrite, todo
}

View File

@ -24,6 +24,7 @@ suite("aggregate_without_roll_up") {
sql "SET enable_nereids_timeout = false"
// tmp disable to rewrite, will be removed in the future
sql "SET disable_nereids_rules = 'INFER_PREDICATES, ELIMINATE_OUTER_JOIN'"
sql "SET global enable_auto_analyze = false"
sql """
drop table if exists orders
@ -170,7 +171,7 @@ suite("aggregate_without_roll_up") {
"max(o_totalprice) as max_total, " +
"min(o_totalprice) as min_total, " +
"count(*) as count_all, " +
"count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end) as cnt_1, " +
"count(distinct case when o_shippriority > 1 and o_orderkey IN (1, 3) then o_custkey else null end), " +
"count(distinct case when O_SHIPPRIORITY > 2 and o_orderkey IN (2) then o_custkey else null end) as cnt_2 " +
"from orders " +
"group by " +
@ -194,7 +195,7 @@ suite("aggregate_without_roll_up") {
sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_0"""
def mv1_1 = "select O_SHIPPRIORITY, O_COMMENT, " +
def mv1_1 = "select O_SHIPPRIORITY, O_COMMENT, O_ORDERDATE, " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (1, 3) then O_ORDERSTATUS else null end) as filter_cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (2) then O_ORDERSTATUS else null end) as filter_cnt_2, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (3, 4) then O_ORDERSTATUS else null end) as filter_cnt_3, " +
@ -218,9 +219,10 @@ suite("aggregate_without_roll_up") {
"from orders " +
"where O_ORDERDATE < '2023-12-30'" +
"group by " +
"O_ORDERDATE, " +
"O_SHIPPRIORITY, " +
"O_COMMENT "
def query1_1 = "select O_SHIPPRIORITY, O_COMMENT, " +
def query1_1 = "select O_ORDERDATE, O_SHIPPRIORITY, O_COMMENT, " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (1, 3) then O_ORDERSTATUS else null end) as filter_cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (2) then O_ORDERSTATUS else null end) as filter_cnt_2, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (3, 4) then O_ORDERSTATUS else null end) as filter_cnt_3, " +
@ -239,13 +241,13 @@ suite("aggregate_without_roll_up") {
"from orders " +
"where O_ORDERDATE < '2023-12-30' and O_ORDERDATE > '2023-12-01'" +
"group by " +
"O_ORDERDATE, " +
"O_SHIPPRIORITY, " +
"O_COMMENT "
// should support but not, tmp
// order_qt_query1_1_before "${query1_1}"
// check_rewrite(mv1_1, query1_1, "mv1_1")
// order_qt_query1_1_after "${query1_1}"
// sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_1"""
order_qt_query1_1_before "${query1_1}"
check_rewrite(mv1_1, query1_1, "mv1_1")
order_qt_query1_1_after "${query1_1}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_1"""
def mv1_2 = "select O_SHIPPRIORITY, O_COMMENT, " +
@ -350,11 +352,11 @@ suite("aggregate_without_roll_up") {
// without group, scalar aggregate
def mv3_0 = "select count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (1, 3) then O_ORDERSTATUS else null end) as filter_cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (2) then O_ORDERSTATUS else null end) as filter_cnt_2, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (3, 4) then O_ORDERSTATUS else null end) as filter_cnt_3, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (2) then O_ORDERSTATUS else null end), " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (3, 4) then O_ORDERSTATUS else null end), " +
"count(distinct case when O_SHIPPRIORITY > 4 and O_ORDERKEY IN (5, 6) then O_ORDERSTATUS else null end) as filter_cnt_4, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (2, 3) then O_ORDERSTATUS else null end) as filter_cnt_5, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (7, 9) then O_ORDERSTATUS else null end) as filter_cnt_6, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (2, 3) then O_ORDERSTATUS else null end), " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (7, 9) then O_ORDERSTATUS else null end), " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (8, 10) then O_ORDERSTATUS else null end) as filter_cnt_7, " +
"count(distinct case when O_SHIPPRIORITY > 4 and O_ORDERKEY IN (11, 13) then O_ORDERSTATUS else null end) as filter_cnt_8, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (12, 11) then O_ORDERSTATUS else null end) as filter_cnt_9, " +
@ -559,17 +561,17 @@ suite("aggregate_without_roll_up") {
def mv17_1 = "select L_ORDERKEY, O_SHIPPRIORITY, O_COMMENT, " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (1, 3) then O_ORDERSTATUS else null end) as filter_cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (2) then O_ORDERSTATUS else null end) as filter_cnt_2, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (3, 4) then O_ORDERSTATUS else null end) as filter_cnt_3, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (3, 4) then O_ORDERSTATUS else null end), " +
"count(distinct case when O_SHIPPRIORITY > 4 and O_ORDERKEY IN (5, 6) then O_ORDERSTATUS else null end) as filter_cnt_4, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (2, 3) then O_ORDERSTATUS else null end) as filter_cnt_5, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (7, 9) then O_ORDERSTATUS else null end) as filter_cnt_6, " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (8, 10) then O_ORDERSTATUS else null end) as filter_cnt_7, " +
"count(distinct case when O_SHIPPRIORITY > 4 and O_ORDERKEY IN (11, 13) then O_ORDERSTATUS else null end) as filter_cnt_8, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (12, 11) then O_ORDERSTATUS else null end) as filter_cnt_9, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (12, 11) then O_ORDERSTATUS else null end), " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (14, 15) then O_ORDERSTATUS else null end) as filter_cnt_10, " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (11, 12) then O_ORDERSTATUS else null end) as filter_cnt_11, " +
"count(distinct case when O_SHIPPRIORITY > 4 and O_ORDERKEY IN (3, 6) then O_ORDERSTATUS else null end) as filter_cnt_12, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (16, 19) then O_ORDERSTATUS else null end) as filter_cnt_13, " +
"count(distinct case when O_SHIPPRIORITY > 3 and O_ORDERKEY IN (16, 19) then O_ORDERSTATUS else null end), " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (20, 3) then O_ORDERSTATUS else null end) as filter_cnt_14, " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (15, 19) then O_ORDERSTATUS else null end) as filter_cnt_15, " +
"count(distinct case when O_SHIPPRIORITY > 1 and O_ORDERKEY IN (13, 21) then O_ORDERSTATUS else null end) as filter_cnt_16, " +
@ -609,11 +611,10 @@ suite("aggregate_without_roll_up") {
"lineitem.L_ORDERKEY, " +
"orders.O_SHIPPRIORITY, " +
"orders.O_COMMENT "
// rewrite success but cbo not chose, tmp
// order_qt_query17_1_before "${query17_1}"
// check_rewrite(mv17_1, query17_1, "mv17_1")
// order_qt_query17_1_after "${query17_1}"
// sql """ DROP MATERIALIZED VIEW IF EXISTS mv17_1"""
order_qt_query17_1_before "${query17_1}"
check_rewrite(mv17_1, query17_1, "mv17_1")
order_qt_query17_1_after "${query17_1}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv17_1"""
// filter outside + left + right
def mv18_0 = "select l_shipdate, l_suppkey, " +
@ -677,7 +678,7 @@ suite("aggregate_without_roll_up") {
// without filter
def mv19_0 = "select o_orderdate, l_partkey, l_suppkey, " +
"sum(o_totalprice) as sum_total, " +
"sum(o_totalprice), " +
"max(o_totalprice) as max_total, " +
"min(o_totalprice) as min_total, " +
"count(*) as count_all " +
@ -760,9 +761,8 @@ suite("aggregate_without_roll_up") {
"orders " +
"on lineitem.L_ORDERKEY = orders.O_ORDERKEY " +
"where orders.O_ORDERDATE < '2023-12-30' and orders.O_ORDERDATE > '2023-12-01' "
// rewrite success but cbo not chose, tmp
// order_qt_query20_0_before "${query20_0}"
// check_rewrite(mv20_0, query20_0, "mv20_0")
// order_qt_query20_0_after "${query20_0}"
// sql """ DROP MATERIALIZED VIEW IF EXISTS mv20_0"""
order_qt_query20_0_before "${query20_0}"
check_rewrite(mv20_0, query20_0, "mv20_0")
order_qt_query20_0_after "${query20_0}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv20_0"""
}

View File

@ -387,11 +387,10 @@ suite("inner_join") {
"from lineitem t1 " +
"inner join (select * from orders where o_orderdate = '2023-12-08') t2 " +
"on t1.l_orderkey = o_orderkey and t1.l_shipdate = o_orderdate "
// should passed but not as isGraphLogicalEquals is false
// order_qt_query6_0_before "${query6_0}"
// check_rewrite(mv6_0, query6_0, "mv6_0")
// order_qt_query6_0_after "${query6_0}"
// sql """ DROP MATERIALIZED VIEW IF EXISTS mv6_0"""
order_qt_query6_0_before "${query6_0}"
check_rewrite(mv6_0, query6_0, "mv6_0")
order_qt_query6_0_after "${query6_0}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv6_0"""
// filter inside + inner + right
@ -405,11 +404,10 @@ suite("inner_join") {
"inner join (select * from orders where o_orderdate = '2023-12-08') t2 " +
"on t1.l_orderkey = o_orderkey and t1.l_shipdate = o_orderdate " +
"where l_partkey = 3"
// should passed but not, because isGraphLogicalEquals is false
// order_qt_query7_0_before "${query7_0}"
// check_rewrite(mv7_0, query7_0, "mv7_0")
// order_qt_query7_0_after "${query7_0}"
// sql """ DROP MATERIALIZED VIEW IF EXISTS mv7_0"""
order_qt_query7_0_before "${query7_0}"
check_rewrite(mv7_0, query7_0, "mv7_0")
order_qt_query7_0_after "${query7_0}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv7_0"""
// check not match, because use a filed orders.O_SHIPPRIORITY which not in mv