diff --git a/fe/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java b/fe/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java index 37136a00dd2808..8626374bf9346e 100644 --- a/fe/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java +++ b/fe/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java @@ -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; @@ -481,7 +482,7 @@ private List checkAndPrepareMaterializedView(CreateMaterializedViewStmt public List checkAndPrepareMaterializedView(AddRollupClause addRollupClause, OlapTable olapTable, long baseIndexId, boolean changeStorageFormat) - throws DdlException { + throws DdlException{ String rollupIndexName = addRollupClause.getRollupName(); List rollupColumnNames = addRollupClause.getColumnNames(); if (changeStorageFormat) { @@ -554,28 +555,52 @@ public List 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. diff --git a/fe/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java b/fe/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java index 56ec28d7693f5c..6f0ff6f75b06c9 100644 --- a/fe/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java +++ b/fe/src/main/java/org/apache/doris/analysis/CreateMaterializedViewStmt.java @@ -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; @@ -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 properties) { + public CreateMaterializedViewStmt(String mvName, SelectStmt selectStmt, Map properties) { this.mvName = mvName; this.selectStmt = selectStmt; this.properties = properties; @@ -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()); } } @@ -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) { @@ -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(); @@ -174,7 +175,7 @@ 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); @@ -182,13 +183,11 @@ public void analyzeSelectClause() throws AnalysisException { 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 @@ -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 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"); @@ -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); @@ -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; diff --git a/fe/src/main/java/org/apache/doris/analysis/CreateTableStmt.java b/fe/src/main/java/org/apache/doris/analysis/CreateTableStmt.java index 9e7cd580ea68a8..ded7c78837e970 100644 --- a/fe/src/main/java/org/apache/doris/analysis/CreateTableStmt.java +++ b/fe/src/main/java/org/apache/doris/analysis/CreateTableStmt.java @@ -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; @@ -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); } diff --git a/fe/src/main/java/org/apache/doris/analysis/MVColumnItem.java b/fe/src/main/java/org/apache/doris/analysis/MVColumnItem.java index a78e216962002e..e92bd4e9c1e653 100644 --- a/fe/src/main/java/org/apache/doris/analysis/MVColumnItem.java +++ b/fe/src/main/java/org/apache/doris/analysis/MVColumnItem.java @@ -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. @@ -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; @@ -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; } diff --git a/fe/src/main/java/org/apache/doris/catalog/Catalog.java b/fe/src/main/java/org/apache/doris/catalog/Catalog.java index 773c76ee103008..da9906c3afc1aa 100755 --- a/fe/src/main/java/org/apache/doris/catalog/Catalog.java +++ b/fe/src/main/java/org/apache/doris/catalog/Catalog.java @@ -4879,29 +4879,30 @@ public static short calcShortKeyColumnCount(List columns, Map FeConstants.shortkey_maxsize_bytes) { - break; - } - if (column.getDataType() == PrimitiveType.VARCHAR) { + int maxShortKeyColumnCount = Math.min(indexColumns.size(), FeConstants.shortkey_max_column_count); + for (int i = 0; i < maxShortKeyColumnCount; i++) { + Column column = indexColumns.get(i); + shortKeySizeByte += column.getOlapColumnIndexSize(); + if (shortKeySizeByte > FeConstants.shortkey_maxsize_bytes) { + if (column.getDataType().isCharFamily()) { ++shortKeyColumnCount; - break; } + break; + } + if (column.getType().isFloatingPointType()) { + break; + } + if (column.getDataType() == PrimitiveType.VARCHAR) { ++shortKeyColumnCount; + break; } + ++shortKeyColumnCount; + } + if (shortKeyColumnCount == 0) { + throw new DdlException("The first column could not be float or double type, use decimal instead"); } - // else - // first column type is VARCHAR - // use only first column as shortKey - // do nothing here } // end calc shortKeyColumnCount diff --git a/fe/src/main/java/org/apache/doris/catalog/PrimitiveType.java b/fe/src/main/java/org/apache/doris/catalog/PrimitiveType.java index caa8e83514b551..0972009d79cde5 100644 --- a/fe/src/main/java/org/apache/doris/catalog/PrimitiveType.java +++ b/fe/src/main/java/org/apache/doris/catalog/PrimitiveType.java @@ -17,15 +17,15 @@ package org.apache.doris.catalog; -import java.util.List; - import org.apache.doris.mysql.MysqlColType; import org.apache.doris.thrift.TPrimitiveType; + import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Lists; import java.util.ArrayList; +import java.util.List; public enum PrimitiveType { INVALID_TYPE("INVALID_TYPE", -1, TPrimitiveType.INVALID_TYPE), @@ -671,6 +671,10 @@ public boolean isStringType() { return (this == VARCHAR || this == CHAR || this == HLL); } + public boolean isCharFamily() { + return (this == VARCHAR || this == CHAR); + } + public boolean isIntegerType() { return (this == TINYINT || this == SMALLINT || this == INT || this == BIGINT); diff --git a/fe/src/main/java/org/apache/doris/catalog/Type.java b/fe/src/main/java/org/apache/doris/catalog/Type.java index e2d8119d7bce7c..4ba15c133c02f0 100644 --- a/fe/src/main/java/org/apache/doris/catalog/Type.java +++ b/fe/src/main/java/org/apache/doris/catalog/Type.java @@ -1030,4 +1030,12 @@ public Type getNumResultType() { public int getStorageLayoutBytes() { return 0; } + + public int getIndexSize() { + if (this.getPrimitiveType() == PrimitiveType.CHAR) { + return ((ScalarType) this).getLength(); + } else { + return this.getPrimitiveType().getOlapColumnIndexSize(); + } + } } diff --git a/fe/src/test/java/org/apache/doris/analysis/CreateMaterializedViewStmtTest.java b/fe/src/test/java/org/apache/doris/analysis/CreateMaterializedViewStmtTest.java index de6c5a30edfdb2..1a778fbdd19001 100644 --- a/fe/src/test/java/org/apache/doris/analysis/CreateMaterializedViewStmtTest.java +++ b/fe/src/test/java/org/apache/doris/analysis/CreateMaterializedViewStmtTest.java @@ -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.Config; import org.apache.doris.common.UserException; import org.apache.doris.common.jmockit.Deencapsulation; @@ -584,16 +585,216 @@ public void testMVColumnsWithoutOrderbyWithoutAggregation(@Injectable SlotRef sl result = columnName3; slotRef4.getColumnName(); result = columnName4; - selectStmt.getResultExprs(); - result = Lists.newArrayList(slotRef1, slotRef2, slotRef3, slotRef4); - slotRef1.getType().getStorageLayoutBytes(); - result = 35; - slotRef2.getType().getStorageLayoutBytes(); + slotRef1.getType().getIndexSize(); + result = 34; + slotRef1.getType().getPrimitiveType(); + result = PrimitiveType.INT; + slotRef2.getType().getIndexSize(); + result = 1; + slotRef2.getType().getPrimitiveType(); + result = PrimitiveType.INT; + slotRef3.getType().getIndexSize(); + result = 1; + slotRef3.getType().getPrimitiveType(); + result = PrimitiveType.INT; + slotRef4.getType().getIndexSize(); + result = 4; + selectStmt.getAggInfo(); // return null, so that the mv can be a duplicate mv + result = null; + } + }; + + + CreateMaterializedViewStmt createMaterializedViewStmt = new CreateMaterializedViewStmt("test", selectStmt, null); + try { + createMaterializedViewStmt.analyze(analyzer); + Assert.assertEquals(KeysType.DUP_KEYS, createMaterializedViewStmt.getMVKeysType()); + List mvColumns = createMaterializedViewStmt.getMVColumnItemList(); + Assert.assertEquals(4, mvColumns.size()); + MVColumnItem mvColumn0 = mvColumns.get(0); + Assert.assertTrue(mvColumn0.isKey()); + Assert.assertFalse(mvColumn0.isAggregationTypeImplicit()); + Assert.assertEquals(columnName1, mvColumn0.getName()); + Assert.assertEquals(null, mvColumn0.getAggregationType()); + MVColumnItem mvColumn1 = mvColumns.get(1); + Assert.assertTrue(mvColumn1.isKey()); + Assert.assertFalse(mvColumn1.isAggregationTypeImplicit()); + Assert.assertEquals(columnName2, mvColumn1.getName()); + Assert.assertEquals(null, mvColumn1.getAggregationType()); + MVColumnItem mvColumn2 = mvColumns.get(2); + Assert.assertTrue(mvColumn2.isKey()); + Assert.assertFalse(mvColumn2.isAggregationTypeImplicit()); + Assert.assertEquals(columnName3, mvColumn2.getName()); + Assert.assertEquals(null, mvColumn2.getAggregationType()); + MVColumnItem mvColumn3 = mvColumns.get(3); + Assert.assertFalse(mvColumn3.isKey()); + Assert.assertTrue(mvColumn3.isAggregationTypeImplicit()); + Assert.assertEquals(columnName4, mvColumn3.getName()); + Assert.assertEquals(AggregateType.NONE, mvColumn3.getAggregationType()); + } catch (UserException e) { + Assert.fail(e.getMessage()); + } + } + + /* + ISSUE: #3811 + */ + @Test + public void testMVColumnsWithoutOrderbyWithoutAggregationWithFloat(@Injectable SlotRef slotRef1, + @Injectable SlotRef slotRef2, @Injectable SlotRef slotRef3, @Injectable SlotRef slotRef4, + @Injectable TableRef tableRef, @Injectable SelectStmt selectStmt) throws UserException { + SelectList selectList = new SelectList(); + SelectListItem selectListItem1 = new SelectListItem(slotRef1, null); + selectList.addItem(selectListItem1); + SelectListItem selectListItem2 = new SelectListItem(slotRef2, null); + selectList.addItem(selectListItem2); + SelectListItem selectListItem3 = new SelectListItem(slotRef3, null); + selectList.addItem(selectListItem3); + SelectListItem selectListItem4 = new SelectListItem(slotRef4, null); + selectList.addItem(selectListItem4); + + final String columnName1 = "k1"; + final String columnName2 = "k2"; + final String columnName3 = "v1"; + final String columnName4 = "v2"; + + new Expectations() { + { + analyzer.getClusterName(); + result = "default"; + selectStmt.getAggInfo(); + result = null; + selectStmt.getSelectList(); + result = selectList; + selectStmt.getTableRefs(); + result = Lists.newArrayList(tableRef); + selectStmt.getWhereClause(); + result = null; + selectStmt.getHavingPred(); + result = null; + selectStmt.getOrderByElements(); + result = null; + selectStmt.getLimit(); + result = -1; + selectStmt.analyze(analyzer); + slotRef1.getColumnName(); + result = columnName1; + slotRef2.getColumnName(); + result = columnName2; + slotRef3.getColumnName(); + result = columnName3; + slotRef4.getColumnName(); + result = columnName4; + slotRef1.getType().getIndexSize(); + result = 1; + slotRef1.getType().getPrimitiveType(); + result = PrimitiveType.INT; + slotRef2.getType().getIndexSize(); result = 2; - slotRef3.getType().getStorageLayoutBytes(); + slotRef2.getType().getPrimitiveType(); + result = PrimitiveType.INT; + slotRef3.getType().getIndexSize(); result = 3; - slotRef4.getType().getStorageLayoutBytes(); - result = 4; + slotRef3.getType().isFloatingPointType(); + result = true; + selectStmt.getAggInfo(); // return null, so that the mv can be a duplicate mv + result = null; + } + }; + + + CreateMaterializedViewStmt createMaterializedViewStmt = new CreateMaterializedViewStmt("test", selectStmt, null); + try { + createMaterializedViewStmt.analyze(analyzer); + Assert.assertEquals(KeysType.DUP_KEYS, createMaterializedViewStmt.getMVKeysType()); + List mvColumns = createMaterializedViewStmt.getMVColumnItemList(); + Assert.assertEquals(4, mvColumns.size()); + MVColumnItem mvColumn0 = mvColumns.get(0); + Assert.assertTrue(mvColumn0.isKey()); + Assert.assertFalse(mvColumn0.isAggregationTypeImplicit()); + Assert.assertEquals(columnName1, mvColumn0.getName()); + Assert.assertEquals(null, mvColumn0.getAggregationType()); + MVColumnItem mvColumn1 = mvColumns.get(1); + Assert.assertTrue(mvColumn1.isKey()); + Assert.assertFalse(mvColumn1.isAggregationTypeImplicit()); + Assert.assertEquals(columnName2, mvColumn1.getName()); + Assert.assertEquals(null, mvColumn1.getAggregationType()); + MVColumnItem mvColumn2 = mvColumns.get(2); + Assert.assertFalse(mvColumn2.isKey()); + Assert.assertTrue(mvColumn2.isAggregationTypeImplicit()); + Assert.assertEquals(columnName3, mvColumn2.getName()); + Assert.assertEquals(AggregateType.NONE, mvColumn2.getAggregationType()); + MVColumnItem mvColumn3 = mvColumns.get(3); + Assert.assertFalse(mvColumn3.isKey()); + Assert.assertTrue(mvColumn3.isAggregationTypeImplicit()); + Assert.assertEquals(columnName4, mvColumn3.getName()); + Assert.assertEquals(AggregateType.NONE, mvColumn3.getAggregationType()); + } catch (UserException e) { + Assert.fail(e.getMessage()); + } + } + + /* + ISSUE: #3811 + */ + @Test + public void testMVColumnsWithoutOrderbyWithoutAggregationWithVarchar(@Injectable SlotRef slotRef1, + @Injectable SlotRef slotRef2, @Injectable SlotRef slotRef3, @Injectable SlotRef slotRef4, + @Injectable TableRef tableRef, @Injectable SelectStmt selectStmt) throws UserException { + SelectList selectList = new SelectList(); + SelectListItem selectListItem1 = new SelectListItem(slotRef1, null); + selectList.addItem(selectListItem1); + SelectListItem selectListItem2 = new SelectListItem(slotRef2, null); + selectList.addItem(selectListItem2); + SelectListItem selectListItem3 = new SelectListItem(slotRef3, null); + selectList.addItem(selectListItem3); + SelectListItem selectListItem4 = new SelectListItem(slotRef4, null); + selectList.addItem(selectListItem4); + + final String columnName1 = "k1"; + final String columnName2 = "k2"; + final String columnName3 = "v1"; + final String columnName4 = "v2"; + + new Expectations() { + { + analyzer.getClusterName(); + result = "default"; + selectStmt.getAggInfo(); + result = null; + selectStmt.getSelectList(); + result = selectList; + selectStmt.getTableRefs(); + result = Lists.newArrayList(tableRef); + selectStmt.getWhereClause(); + result = null; + selectStmt.getHavingPred(); + result = null; + selectStmt.getOrderByElements(); + result = null; + selectStmt.getLimit(); + result = -1; + selectStmt.analyze(analyzer); + slotRef1.getColumnName(); + result = columnName1; + slotRef2.getColumnName(); + result = columnName2; + slotRef3.getColumnName(); + result = columnName3; + slotRef4.getColumnName(); + result = columnName4; + slotRef1.getType().getIndexSize(); + result = 1; + slotRef1.getType().getPrimitiveType(); + result = PrimitiveType.INT; + slotRef2.getType().getIndexSize(); + result = 2; + slotRef2.getType().getPrimitiveType(); + result = PrimitiveType.INT; + slotRef3.getType().getIndexSize(); + result = 3; + slotRef3.getType().getPrimitiveType(); + result = PrimitiveType.VARCHAR; selectStmt.getAggInfo(); // return null, so that the mv can be a duplicate mv result = null; } @@ -631,6 +832,110 @@ public void testMVColumnsWithoutOrderbyWithoutAggregation(@Injectable SlotRef sl } } + /* + ISSUE: #3811 + */ + @Test + public void testMVColumnsWithFirstFloat(@Injectable SlotRef slotRef1, + @Injectable TableRef tableRef, @Injectable SelectStmt selectStmt) throws UserException { + SelectList selectList = new SelectList(); + SelectListItem selectListItem1 = new SelectListItem(slotRef1, null); + selectList.addItem(selectListItem1); + + final String columnName1 = "k1"; + + new Expectations() { + { + analyzer.getClusterName(); + result = "default"; + selectStmt.getAggInfo(); + result = null; + selectStmt.getSelectList(); + result = selectList; + selectStmt.getTableRefs(); + result = Lists.newArrayList(tableRef); + selectStmt.getWhereClause(); + result = null; + selectStmt.getHavingPred(); + result = null; + selectStmt.getOrderByElements(); + result = null; + selectStmt.analyze(analyzer); + slotRef1.getColumnName(); + result = columnName1; + slotRef1.getType().isFloatingPointType(); + result = true; + selectStmt.getAggInfo(); // return null, so that the mv can be a duplicate mv + result = null; + } + }; + + + CreateMaterializedViewStmt createMaterializedViewStmt = new CreateMaterializedViewStmt("test", selectStmt, null); + try { + createMaterializedViewStmt.analyze(analyzer); + Assert.fail("The first column could not be float or double, use decimal instead"); + } catch (UserException e) { + System.out.print(e.getMessage()); + } + } + + /* + ISSUE: #3811 + */ + @Test + public void testMVColumnsWithFirstVarchar(@Injectable SlotRef slotRef1, + @Injectable TableRef tableRef, @Injectable SelectStmt selectStmt) throws UserException { + SelectList selectList = new SelectList(); + SelectListItem selectListItem1 = new SelectListItem(slotRef1, null); + selectList.addItem(selectListItem1); + + final String columnName1 = "k1"; + + new Expectations() { + { + analyzer.getClusterName(); + result = "default"; + selectStmt.getAggInfo(); + result = null; + selectStmt.getSelectList(); + result = selectList; + selectStmt.getTableRefs(); + result = Lists.newArrayList(tableRef); + selectStmt.getWhereClause(); + result = null; + selectStmt.getHavingPred(); + result = null; + selectStmt.getOrderByElements(); + result = null; + selectStmt.getLimit(); + result = -1; + selectStmt.analyze(analyzer); + slotRef1.getColumnName(); + result = columnName1; + slotRef1.getType().getPrimitiveType(); + result = PrimitiveType.VARCHAR; + } + }; + + + CreateMaterializedViewStmt createMaterializedViewStmt = new CreateMaterializedViewStmt("test", selectStmt, null); + try { + createMaterializedViewStmt.analyze(analyzer); + Assert.assertEquals(KeysType.DUP_KEYS, createMaterializedViewStmt.getMVKeysType()); + List mvColumns = createMaterializedViewStmt.getMVColumnItemList(); + Assert.assertEquals(1, mvColumns.size()); + MVColumnItem mvColumn0 = mvColumns.get(0); + Assert.assertTrue(mvColumn0.isKey()); + Assert.assertFalse(mvColumn0.isAggregationTypeImplicit()); + Assert.assertEquals(columnName1, mvColumn0.getName()); + Assert.assertEquals(null, mvColumn0.getAggregationType()); + } catch (UserException e) { + Assert.fail(e.getMessage()); + } + } + + @Test public void testMVColumns(@Injectable SlotRef slotRef1, @Injectable SlotRef slotRef2, diff --git a/fe/src/test/java/org/apache/doris/planner/QueryPlanTest.java b/fe/src/test/java/org/apache/doris/planner/QueryPlanTest.java index dc05ec54ce1a6e..9195490e1957c2 100644 --- a/fe/src/test/java/org/apache/doris/planner/QueryPlanTest.java +++ b/fe/src/test/java/org/apache/doris/planner/QueryPlanTest.java @@ -69,9 +69,9 @@ public static void beforeClass() throws Exception { Catalog.getCurrentCatalog().createDb(createDbStmt); createTable("create table test.test1\n" + - "(\n" + - " query_id varchar(48) comment \"Unique query id\",\n" + - " time datetime not null comment \"Query start time\",\n" + + "(\n" + + " time datetime not null comment \"Query start time\",\n" + + " query_id varchar(48) comment \"Unique query id\",\n" + " client_ip varchar(32) comment \"Client IP\",\n" + " user varchar(64) comment \"User name\",\n" + " db varchar(96) comment \"Database of this query\",\n" + diff --git a/fe/src/test/java/org/apache/doris/utframe/DorisAssert.java b/fe/src/test/java/org/apache/doris/utframe/DorisAssert.java index b808fecec564ea..8ba14dab4dda0f 100644 --- a/fe/src/test/java/org/apache/doris/utframe/DorisAssert.java +++ b/fe/src/test/java/org/apache/doris/utframe/DorisAssert.java @@ -141,11 +141,15 @@ public void explainWithout(String s) throws Exception { } public String explainQuery() throws Exception { - StmtExecutor stmtExecutor = new StmtExecutor(connectContext, "explain " + sql); + return internalExecute("explain " + sql); + } + + private String internalExecute(String sql) throws Exception { + StmtExecutor stmtExecutor = new StmtExecutor(connectContext, sql); stmtExecutor.execute(); QueryState queryState = connectContext.getState(); if (queryState.getStateType() == QueryState.MysqlStateType.ERR) { - switch (queryState.getErrType()){ + switch (queryState.getErrType()) { case ANALYSIS_ERR: throw new AnalysisException(queryState.getErrorMessage()); case OTHER_ERR: