[Enchancement](Materialized-View) forbiden some case in create mv with group by and fix select fail on g… (#16820)
1. forbiden some case in create mv with group by select k1+1,sum(abs(k2+2)+k3+3) from d_table group by k1; 2. fix select fail on grouping column have diffrent expr with select list create materialized view k1p2ap3psg as select k1+1,sum(abs(k2+2)+k3+3) from d_table group by k1+1; mysql [test]>explain select k1+1,sum(abs(k2+2)+k3+3) from d_table group by k1; ERROR 1105 (HY000): errCode = 2, detailMessage = select list expression not produced by aggregation output (missing from GROUP BY clause?): `k1` + 1
This commit is contained in:
@ -158,6 +158,7 @@ public class CreateMaterializedViewStmt extends DdlStmt {
|
||||
+ selectStmt.getHavingPred().toSql());
|
||||
}
|
||||
analyzeOrderByClause();
|
||||
analyzeGroupByClause();
|
||||
if (selectStmt.getLimit() != -1) {
|
||||
throw new AnalysisException("The limit clause is not supported in add materialized view clause, expr:"
|
||||
+ " limit " + selectStmt.getLimit());
|
||||
@ -239,6 +240,39 @@ public class CreateMaterializedViewStmt extends DdlStmt {
|
||||
dbName = tableName.getDb();
|
||||
}
|
||||
|
||||
private void analyzeGroupByClause() throws AnalysisException {
|
||||
if (selectStmt.getGroupByClause() == null) {
|
||||
return;
|
||||
}
|
||||
List<Expr> groupingExprs = selectStmt.getGroupByClause().getGroupingExprs();
|
||||
List<FunctionCallExpr> aggregateExprs = selectStmt.getAggInfo().getAggregateExprs();
|
||||
List<Expr> selectExprs = selectStmt.getSelectList().getExprs();
|
||||
for (Expr expr : selectExprs) {
|
||||
boolean match = false;
|
||||
String lhs = selectStmt.getExprFromAliasSMap(expr).toSqlWithoutTbl();
|
||||
for (Expr groupExpr : groupingExprs) {
|
||||
String rhs = selectStmt.getExprFromAliasSMap(groupExpr).toSqlWithoutTbl();
|
||||
if (lhs.equalsIgnoreCase(rhs)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
for (Expr groupExpr : aggregateExprs) {
|
||||
String rhs = selectStmt.getExprFromAliasSMap(groupExpr).toSqlWithoutTbl();
|
||||
if (lhs.equalsIgnoreCase(rhs)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
throw new AnalysisException("The select expr " + lhs + " not in grouping or aggregate columns");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void analyzeOrderByClause() throws AnalysisException {
|
||||
if (selectStmt.getOrderByElements() == null) {
|
||||
supplyOrderColumn();
|
||||
@ -513,16 +547,20 @@ public class CreateMaterializedViewStmt extends DdlStmt {
|
||||
return name;
|
||||
}
|
||||
|
||||
private static boolean mvMatch(String name, String prefix) {
|
||||
return MaterializedIndexMeta.normalizeName(name).startsWith(prefix);
|
||||
}
|
||||
|
||||
public static boolean isMVColumn(String name) {
|
||||
return isMVColumnAggregate(name) || isMVColumnNormal(name);
|
||||
}
|
||||
|
||||
public static boolean isMVColumnAggregate(String name) {
|
||||
return name.startsWith(MATERIALIZED_VIEW_AGGREGATE_NAME_PREFIX);
|
||||
return mvMatch(name, MATERIALIZED_VIEW_AGGREGATE_NAME_PREFIX);
|
||||
}
|
||||
|
||||
public static boolean isMVColumnNormal(String name) {
|
||||
return name.startsWith(MATERIALIZED_VIEW_NAME_PREFIX);
|
||||
return mvMatch(name, MATERIALIZED_VIEW_NAME_PREFIX);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -46,6 +46,7 @@ import org.apache.doris.common.util.SqlUtils;
|
||||
import org.apache.doris.mysql.privilege.PrivPredicate;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
import org.apache.doris.rewrite.ExprRewriter;
|
||||
import org.apache.doris.rewrite.mvrewrite.MVSelectFailedException;
|
||||
import org.apache.doris.thrift.TExprOpcode;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
@ -1491,9 +1492,19 @@ public class SelectStmt extends QueryStmt {
|
||||
// check that all post-agg exprs point to agg output
|
||||
for (int i = 0; i < selectList.getItems().size(); ++i) {
|
||||
if (!resultExprs.get(i).isBoundByTupleIds(groupingByTupleIds)) {
|
||||
throw new AnalysisException(
|
||||
"select list expression not produced by aggregation output " + "(missing from "
|
||||
+ "GROUP BY clause?): " + selectList.getItems().get(i).getExpr().toSql());
|
||||
if (CreateMaterializedViewStmt.isMVColumn(resultExprs.get(i).toSqlWithoutTbl())) {
|
||||
List<TupleId> tupleIds = Lists.newArrayList();
|
||||
List<SlotId> slotIds = Lists.newArrayList();
|
||||
resultExprs.get(i).getIds(tupleIds, slotIds);
|
||||
for (TupleId id : tupleIds) {
|
||||
updateDisableTuplesMVRewriter(id);
|
||||
}
|
||||
throw new MVSelectFailedException("Materialized View rewrite invalid");
|
||||
} else {
|
||||
throw new AnalysisException(
|
||||
"select list expression not produced by aggregation output " + "(missing from "
|
||||
+ "GROUP BY clause?): " + selectList.getItems().get(i).getExpr().toSql());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (orderByElements != null) {
|
||||
|
||||
@ -259,6 +259,11 @@ public class ExprToSlotRefRule implements ExprRewriteRule {
|
||||
|
||||
if (mvColumn == null) {
|
||||
return expr;
|
||||
} else {
|
||||
Expr rhs = mvColumn.getDefineExpr();
|
||||
if (rhs == null || !rhs.getClass().equals(expr.getClass())) {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
return rewriteExpr(tableName, mvColumn, analyzer);
|
||||
|
||||
@ -17,9 +17,9 @@
|
||||
|
||||
package org.apache.doris.rewrite.mvrewrite;
|
||||
|
||||
import org.apache.doris.common.UserException;
|
||||
import org.apache.doris.common.AnalysisException;
|
||||
|
||||
public class MVSelectFailedException extends UserException {
|
||||
public class MVSelectFailedException extends AnalysisException {
|
||||
|
||||
public MVSelectFailedException(String msg) {
|
||||
super(msg);
|
||||
|
||||
@ -561,6 +561,8 @@ public class CreateMaterializedViewStmtTest {
|
||||
result = columnName3;
|
||||
slotRef4.toSql();
|
||||
result = columnName4;
|
||||
selectStmt.getGroupByClause();
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -664,6 +666,8 @@ public class CreateMaterializedViewStmtTest {
|
||||
result = 4;
|
||||
selectStmt.getAggInfo(); // return null, so that the mv can be a duplicate mv
|
||||
result = null;
|
||||
selectStmt.getGroupByClause();
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -762,6 +766,8 @@ public class CreateMaterializedViewStmtTest {
|
||||
result = true;
|
||||
selectStmt.getAggInfo(); // return null, so that the mv can be a duplicate mv
|
||||
result = null;
|
||||
selectStmt.getGroupByClause();
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -860,6 +866,8 @@ public class CreateMaterializedViewStmtTest {
|
||||
result = PrimitiveType.VARCHAR;
|
||||
selectStmt.getAggInfo(); // return null, so that the mv can be a duplicate mv
|
||||
result = null;
|
||||
selectStmt.getGroupByClause();
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -964,6 +972,8 @@ public class CreateMaterializedViewStmtTest {
|
||||
result = columnName1;
|
||||
slotRef1.getType().getPrimitiveType();
|
||||
result = PrimitiveType.VARCHAR;
|
||||
selectStmt.getGroupByClause();
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1040,6 +1050,8 @@ public class CreateMaterializedViewStmtTest {
|
||||
result = columnName1;
|
||||
slotRef2.getColumnName();
|
||||
result = columnName2;
|
||||
selectStmt.getGroupByClause();
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1095,6 +1107,8 @@ public class CreateMaterializedViewStmtTest {
|
||||
result = null;
|
||||
selectStmt.getHavingPred();
|
||||
result = null;
|
||||
selectStmt.getGroupByClause();
|
||||
result = null;
|
||||
selectStmt.getLimit();
|
||||
result = -1;
|
||||
slotRef1.toSql();
|
||||
|
||||
@ -23,3 +23,6 @@
|
||||
2 2
|
||||
3 -3
|
||||
|
||||
-- !select_mv --
|
||||
\N -4
|
||||
|
||||
|
||||
@ -10,3 +10,8 @@
|
||||
3 7
|
||||
5 9
|
||||
|
||||
-- !select_base --
|
||||
1 1
|
||||
3 7
|
||||
5 9
|
||||
|
||||
|
||||
@ -69,4 +69,10 @@ suite ("agg_have_dup_base") {
|
||||
contains "(k12s3m)"
|
||||
}
|
||||
qt_select_mv "select k1,max(k2) from d_table group by k1 order by k1;"
|
||||
|
||||
explain {
|
||||
sql("select unix_timestamp(k1) tmp,sum(k2) from d_table group by tmp;")
|
||||
contains "(k12s3m)"
|
||||
}
|
||||
qt_select_mv "select unix_timestamp(k1) tmp,sum(k2) from d_table group by tmp order by tmp;"
|
||||
}
|
||||
|
||||
@ -36,6 +36,14 @@ suite ("multi_slot_k1a2p2ap3ps") {
|
||||
sql "insert into d_table select 2,2,2,'b';"
|
||||
sql "insert into d_table select 3,-3,null,'c';"
|
||||
|
||||
boolean createFail = false;
|
||||
try {
|
||||
sql "create materialized view k1a2p2ap3ps as select abs(k1)+k2+1,sum(abs(k2+2)+k3+3) from d_table group by abs(k1)+k2;"
|
||||
} catch (Exception e) {
|
||||
createFail = true;
|
||||
}
|
||||
assertTrue(createFail);
|
||||
|
||||
def result = "null"
|
||||
sql "create materialized view k1a2p2ap3ps as select abs(k1)+k2+1,sum(abs(k2+2)+k3+3) from d_table group by abs(k1)+k2+1;"
|
||||
while (!result.contains("FINISHED")){
|
||||
@ -57,4 +65,10 @@ suite ("multi_slot_k1a2p2ap3ps") {
|
||||
contains "(k1a2p2ap3ps)"
|
||||
}
|
||||
qt_select_mv "select abs(k1)+k2+1,sum(abs(k2+2)+k3+3) from d_table group by abs(k1)+k2+1 order by abs(k1)+k2+1;"
|
||||
|
||||
explain {
|
||||
sql("select abs(k1)+k2+1,sum(abs(k2+2)+k3+3) from d_table group by abs(k1)+k2 order by abs(k1)+k2")
|
||||
contains "(d_table)"
|
||||
}
|
||||
qt_select_base "select abs(k1)+k2+1,sum(abs(k2+2)+k3+3) from d_table group by abs(k1)+k2 order by abs(k1)+k2;"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user