Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.OlapTable.OlapTableState;
import org.apache.doris.catalog.Partition;
import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.catalog.Replica;
import org.apache.doris.catalog.Table;
import org.apache.doris.catalog.Tablet;
Expand Down Expand Up @@ -481,7 +482,7 @@ private List<Column> checkAndPrepareMaterializedView(CreateMaterializedViewStmt

public List<Column> checkAndPrepareMaterializedView(AddRollupClause addRollupClause, OlapTable olapTable,
long baseIndexId, boolean changeStorageFormat)
throws DdlException {
throws DdlException{
String rollupIndexName = addRollupClause.getRollupName();
List<String> rollupColumnNames = addRollupClause.getColumnNames();
if (changeStorageFormat) {
Expand Down Expand Up @@ -554,28 +555,52 @@ public List<Column> checkAndPrepareMaterializedView(AddRollupClause addRollupCla
} else if (KeysType.DUP_KEYS == keysType) {
// supplement the duplicate key
if (addRollupClause.getDupKeys() == null || addRollupClause.getDupKeys().isEmpty()) {
int keyStorageLayoutBytes = 0;
// check the column meta
for (int i = 0; i < rollupColumnNames.size(); i++) {
String columnName = rollupColumnNames.get(i);
Column baseColumn = baseColumnNameToColumn.get(columnName);
if (baseColumn == null) {
throw new DdlException("Column[" + columnName + "] does not exist in base index");
}
keyStorageLayoutBytes += baseColumn.getType().getStorageLayoutBytes();
Column rollupColumn = new Column(baseColumn);
if(changeStorageFormat) {
rollupColumn.setIsKey(baseColumn.isKey());
rollupColumn.setAggregationType(baseColumn.getAggregationType(), true);
} else if ((i + 1) <= FeConstants.shortkey_max_column_count
|| keyStorageLayoutBytes < FeConstants.shortkey_maxsize_bytes) {
rollupColumn.setIsKey(true);
rollupColumn.setAggregationType(null, false);
} else {
rollupColumn.setIsKey(false);
rollupColumn.setAggregationType(AggregateType.NONE, true);
}
rollupSchema.add(rollupColumn);
}
if (changeStorageFormat) {
return rollupSchema;
}
// Supplement key of MV columns
int theBeginIndexOfValue = 0;
int keySizeByte = 0;
for (; theBeginIndexOfValue < rollupSchema.size(); theBeginIndexOfValue++) {
Column column = rollupSchema.get(theBeginIndexOfValue);
keySizeByte += column.getType().getIndexSize();
if (theBeginIndexOfValue + 1 > FeConstants.shortkey_max_column_count
|| keySizeByte > FeConstants.shortkey_maxsize_bytes) {
if (theBeginIndexOfValue == 0 && column.getType().getPrimitiveType().isCharFamily()) {
column.setIsKey(true);
theBeginIndexOfValue++;
}
break;
}
if (column.getType().isFloatingPointType()) {
break;
}
if (column.getType().getPrimitiveType() == PrimitiveType.VARCHAR) {
column.setIsKey(true);
theBeginIndexOfValue++;
break;
}
column.setIsKey(true);
}
if (theBeginIndexOfValue == 0) {
throw new DdlException("The first column could not be float or double");
}
// Supplement value of MV columns
for (; theBeginIndexOfValue < rollupSchema.size(); theBeginIndexOfValue++) {
Column rollupColumn = rollupSchema.get(theBeginIndexOfValue);
rollupColumn.setIsKey(false);
rollupColumn.setAggregationType(AggregateType.NONE, true);
}
} else {
/*
* eg.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.apache.doris.catalog.AggregateType;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
import org.apache.doris.common.ErrorCode;
Expand Down Expand Up @@ -69,8 +70,7 @@ public class CreateMaterializedViewStmt extends DdlStmt {
private String dbName;
private KeysType mvKeysType = KeysType.DUP_KEYS;

public CreateMaterializedViewStmt(String mvName, SelectStmt selectStmt,
Map<String, String> properties) {
public CreateMaterializedViewStmt(String mvName, SelectStmt selectStmt, Map<String, String> properties) {
this.mvName = mvName;
this.selectStmt = selectStmt;
this.properties = properties;
Expand Down Expand Up @@ -116,16 +116,16 @@ public void analyze(Analyzer analyzer) throws UserException {
analyzeFromClause();
if (selectStmt.getWhereClause() != null) {
throw new AnalysisException("The where clause is not supported in add materialized view clause, expr:"
+ selectStmt.getWhereClause().toSql());
+ selectStmt.getWhereClause().toSql());
}
if (selectStmt.getHavingPred() != null) {
throw new AnalysisException("The having clause is not supported in add materialized view clause, expr:"
+ selectStmt.getHavingPred().toSql());
+ selectStmt.getHavingPred().toSql());
}
analyzeOrderByClause();
if (selectStmt.getLimit() != -1) {
throw new AnalysisException("The limit clause is not supported in add materialized view clause, expr:"
+ " limit " + selectStmt.getLimit());
+ " limit " + selectStmt.getLimit());
}
}

Expand All @@ -151,7 +151,7 @@ public void analyzeSelectClause() throws AnalysisException {
Expr selectListItemExpr = selectListItem.getExpr();
if (!(selectListItemExpr instanceof SlotRef) && !(selectListItemExpr instanceof FunctionCallExpr)) {
throw new AnalysisException("The materialized view only support the single column or function expr. "
+ "Error column: " + selectListItemExpr.toSql());
+ "Error column: " + selectListItemExpr.toSql());
}
if (selectListItem.getExpr() instanceof SlotRef) {
if (meetAggregate) {
Expand All @@ -164,6 +164,7 @@ public void analyzeSelectClause() throws AnalysisException {
ErrorReport.reportAnalysisException(ErrorCode.ERR_DUP_FIELDNAME, columnName);
}
MVColumnItem mvColumnItem = new MVColumnItem(columnName);
mvColumnItem.setType(slotRef.getType());
mvColumnItemList.add(mvColumnItem);
} else if (selectListItem.getExpr() instanceof FunctionCallExpr) {
FunctionCallExpr functionCallExpr = (FunctionCallExpr) selectListItem.getExpr();
Expand All @@ -174,21 +175,19 @@ public void analyzeSelectClause() throws AnalysisException {
&& !functionName.equalsIgnoreCase("min")
&& !functionName.equalsIgnoreCase("max")) {
throw new AnalysisException("The materialized view only support the sum, min and max aggregate "
+ "function. Error function: " + functionCallExpr.toSqlImpl());
+ "function. Error function: " + functionCallExpr.toSqlImpl());
}

Preconditions.checkState(functionCallExpr.getChildren().size() == 1);
Expr functionChild0 = functionCallExpr.getChild(0);
SlotRef slotRef;
if (functionChild0 instanceof SlotRef) {
slotRef = (SlotRef) functionChild0;
}
else if (functionChild0 instanceof CastExpr
&& (functionChild0.getChild(0) instanceof SlotRef)) {
} else if (functionChild0 instanceof CastExpr && (functionChild0.getChild(0) instanceof SlotRef)) {
slotRef = (SlotRef) functionChild0.getChild(0);
} else {
throw new AnalysisException("The children of aggregate function only support one original column. "
+ "Error function: " + functionCallExpr.toSqlImpl());
+ "Error function: " + functionCallExpr.toSqlImpl());
}
meetAggregate = true;
// check duplicate column
Expand Down Expand Up @@ -226,49 +225,14 @@ private void analyzeFromClause() throws AnalysisException {

private void analyzeOrderByClause() throws AnalysisException {
if (selectStmt.getOrderByElements() == null) {
/**
* The keys type of Materialized view is aggregation.
* All of group by columns are keys of materialized view.
*/
if (mvKeysType == KeysType.AGG_KEYS) {
for (MVColumnItem mvColumnItem : mvColumnItemList) {
if (mvColumnItem.getAggregationType() != null) {
break;
}
mvColumnItem.setIsKey(true);
}
return;
}

/**
* There is no aggregation function in materialized view.
* Supplement key of MV columns
* For example: select k1, k2 ... kn from t1
* The default key columns are first 36 bytes of the columns in define order.
* If the number of columns in the first 36 is less than 3, the first 3 columns will be used.
* column: k1, k2, k3... km. The key is true.
* Supplement non-key of MV columns
* column: km... kn. The key is false, aggregation type is none, isAggregationTypeImplicit is true.
*/
int keyStorageLayoutBytes = 0;
for (int i = 0; i < selectStmt.getResultExprs().size(); i++) {
MVColumnItem mvColumnItem = mvColumnItemList.get(i);
Expr resultColumn = selectStmt.getResultExprs().get(i);
keyStorageLayoutBytes += resultColumn.getType().getStorageLayoutBytes();
if ((i + 1) <= FeConstants.shortkey_max_column_count
|| keyStorageLayoutBytes < FeConstants.shortkey_maxsize_bytes) {
mvColumnItem.setIsKey(true);
} else {
mvColumnItem.setAggregationType(AggregateType.NONE, true);
}
}
supplyOrderColumn();
return;
}

List<OrderByElement> orderByElements = selectStmt.getOrderByElements();
if (orderByElements.size() > mvColumnItemList.size()) {
throw new AnalysisException("The number of columns in order clause must be less then "
+ "the number of columns in select clause");
throw new AnalysisException("The number of columns in order clause must be less then " + "the number of "
+ "columns in select clause");
}
if (beginIndexOfAggregation != -1 && (orderByElements.size() != (beginIndexOfAggregation))) {
throw new AnalysisException("The key of columns in mv must be all of group by columns");
Expand All @@ -277,13 +241,13 @@ private void analyzeOrderByClause() throws AnalysisException {
Expr orderByElement = orderByElements.get(i).getExpr();
if (!(orderByElement instanceof SlotRef)) {
throw new AnalysisException("The column in order clause must be original column without calculation. "
+ "Error column: " + orderByElement.toSql());
+ "Error column: " + orderByElement.toSql());
}
MVColumnItem mvColumnItem = mvColumnItemList.get(i);
SlotRef slotRef = (SlotRef) orderByElement;
if (!mvColumnItem.getName().equalsIgnoreCase(slotRef.getColumnName())) {
throw new AnalysisException("The order of columns in order by clause must be same as "
+ "the order of columns in select list");
+ "the order of columns in select list");
}
Preconditions.checkState(mvColumnItem.getAggregationType() == null);
mvColumnItem.setIsKey(true);
Expand All @@ -301,6 +265,69 @@ private void analyzeOrderByClause() throws AnalysisException {
}
}

/*
This function is used to supply order by columns and calculate short key count
*/
private void supplyOrderColumn() throws AnalysisException {
/**
* The keys type of Materialized view is aggregation.
* All of group by columns are keys of materialized view.
*/
if (mvKeysType == KeysType.AGG_KEYS) {
for (MVColumnItem mvColumnItem : mvColumnItemList) {
if (mvColumnItem.getAggregationType() != null) {
break;
}
mvColumnItem.setIsKey(true);
}
} else if (mvKeysType == KeysType.DUP_KEYS) {
/**
* There is no aggregation function in materialized view.
* Supplement key of MV columns
* The key is same as the short key in duplicate table
* For example: select k1, k2 ... kn from t1
* The default key columns are first 36 bytes of the columns in define order.
* If the number of columns in the first 36 is more than 3, the first 3 columns will be used.
* column: k1, k2, k3. The key is true.
* Supplement non-key of MV columns
* column: k4... kn. The key is false, aggregation type is none, isAggregationTypeImplicit is true.
*/
int theBeginIndexOfValue = 0;
// supply key
int keySizeByte = 0;
for (; theBeginIndexOfValue < mvColumnItemList.size(); theBeginIndexOfValue++) {
MVColumnItem column = mvColumnItemList.get(theBeginIndexOfValue);
keySizeByte += column.getType().getIndexSize();
if (theBeginIndexOfValue + 1 > FeConstants.shortkey_max_column_count
|| keySizeByte > FeConstants.shortkey_maxsize_bytes) {
if (theBeginIndexOfValue == 0 && column.getType().getPrimitiveType().isCharFamily()) {
column.setIsKey(true);
theBeginIndexOfValue++;
}
break;
}
if (column.getType().isFloatingPointType()) {
break;
}
if (column.getType().getPrimitiveType() == PrimitiveType.VARCHAR) {
column.setIsKey(true);
theBeginIndexOfValue++;
break;
}
column.setIsKey(true);
}
if (theBeginIndexOfValue == 0) {
throw new AnalysisException("The first column could not be float or double type, use decimal instead");
}
// supply value
for (; theBeginIndexOfValue < mvColumnItemList.size(); theBeginIndexOfValue++) {
MVColumnItem mvColumnItem = mvColumnItemList.get(theBeginIndexOfValue);
mvColumnItem.setAggregationType(AggregateType.NONE, true);
}
}

}

@Override
public String toSql() {
return null;
Expand Down
25 changes: 22 additions & 3 deletions fe/src/main/java/org/apache/doris/analysis/CreateTableStmt.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.doris.catalog.Index;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.PartitionType;
import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
import org.apache.doris.common.ErrorCode;
Expand Down Expand Up @@ -283,11 +284,29 @@ public void analyze(Analyzer analyzer) throws UserException {
keysDesc = new KeysDesc(KeysType.AGG_KEYS, keysColumnNames);
} else {
for (ColumnDef columnDef : columnDefs) {
keyLength += columnDef.getType().getStorageLayoutBytes();
if (keysColumnNames.size() < FeConstants.shortkey_max_column_count
|| keyLength < FeConstants.shortkey_maxsize_bytes) {
keyLength += columnDef.getType().getIndexSize();
if (keysColumnNames.size() >= FeConstants.shortkey_max_column_count
|| keyLength > FeConstants.shortkey_maxsize_bytes) {
if (keysColumnNames.size() == 0
&& columnDef.getType().getPrimitiveType().isCharFamily()) {
keysColumnNames.add(columnDef.getName());
}
break;
}
if (columnDef.getType().isFloatingPointType()) {
break;
}
if (columnDef.getType().getPrimitiveType() == PrimitiveType.VARCHAR) {
keysColumnNames.add(columnDef.getName());
break;
}
keysColumnNames.add(columnDef.getName());
}
// The OLAP table must has at least one short key and the float and double should not be short key.
// So the float and double could not be the first column in OLAP table.
if (keysColumnNames.isEmpty()) {
throw new AnalysisException("The first column could not be float or double,"
+ " use decimal instead.");
}
keysDesc = new KeysDesc(KeysType.DUP_KEYS, keysColumnNames);
}
Expand Down
11 changes: 11 additions & 0 deletions fe/src/main/java/org/apache/doris/analysis/MVColumnItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package org.apache.doris.analysis;

import org.apache.doris.catalog.AggregateType;
import org.apache.doris.catalog.Type;

/**
* This is a result of semantic analysis for AddMaterializedViewClause.
Expand All @@ -27,6 +28,8 @@
*/
public class MVColumnItem {
private String name;
// the origin type of slot ref
private Type type;
private boolean isKey;
private AggregateType aggregationType;
private boolean isAggregationTypeImplicit;
Expand All @@ -40,6 +43,14 @@ public String getName() {
return name;
}

public Type getType() {
return type;
}

public void setType(Type type) {
this.type = type;
}

public void setIsKey(boolean key) {
isKey = key;
}
Expand Down
Loading