You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by GitBox <gi...@apache.org> on 2019/12/13 07:31:31 UTC

[GitHub] [incubator-doris] morningman commented on a change in pull request #2431: Support to create materialized view

morningman commented on a change in pull request #2431: Support to create materialized view
URL: https://github.com/apache/incubator-doris/pull/2431#discussion_r357499220
 
 

 ##########
 File path: fe/src/main/java/org/apache/doris/analysis/AddMaterializedViewClause.java
 ##########
 @@ -0,0 +1,233 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.analysis;
+
+import org.apache.doris.catalog.AggregateType;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.FeNameFormat;
+import org.apache.doris.common.UserException;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Materialized view is performed to materialize the results of query.
+ * This clause is used to create a new materialized view for a specified table
+ * through a specified query stmt.
+ * <p>
+ * Syntax:
+ * ALTER TABLE [Table name] ADD Materialized View [MV name] (
+ *     SELECT select_expr[, select_expr ...]
+ *     FROM [Base view name]
+ *     GROUP BY column_name[, column_name ...]
+ *     ORDER BY column_name[, column_name ...])
+ * [PROPERTIES ("key": "value")]
+ */
+public class AddMaterializedViewClause extends AlterClause {
+    private String mvName;
+    private SelectStmt selectStmt;
+    private Map<String, String> properties;
+
+    /**
+     * origin stmt: select k1, k2, v1, sum(v2) from base_table group by k1, k2, v1 order by k1, k2
+     * mvColumnItemList: [k1: {name: k1, isKey: true, aggType: null, isAggregationTypeImplicit: false},
+     *                    k2: {name: k2, isKey: true, aggType: null, isAggregationTypeImplicit: false},
+     *                    v1: {name: v1, isKey: false, aggType: none, isAggregationTypeImplicit: true},
+     *                    v2: {name: v2, isKey: false, aggType: sum, isAggregationTypeImplicit: false}]
+     * This order of mvColumnItemList is meaningful.
+     */
+    private List<MVColumnItem> mvColumnItemList = Lists.newArrayList();
+    private String baseIndexName;
+
+    public AddMaterializedViewClause(String mvName, SelectStmt selectStmt,
+                                     Map<String, String> properties) {
+        this.mvName = mvName;
+        this.selectStmt = selectStmt;
+        this.properties = properties;
+    }
+
+    public String getMVName() {
+        return mvName;
+    }
+
+    public List<MVColumnItem> getMVColumnItemList() {
+        return mvColumnItemList;
+    }
+
+    public String getBaseIndexName() {
+        return baseIndexName;
+    }
+
+    @Override
+    public void analyze(Analyzer analyzer) throws UserException {
+        FeNameFormat.checkTableName(mvName);
+        // TODO(ml): the mv name in from clause should pass the analyze without error.
+        selectStmt.analyze(analyzer);
+        analyzeSelectClause();
+        analyzeFromClause();
+        if (selectStmt.getWhereClause() != null) {
+            throw new AnalysisException("The where clause is not supported in add materialized view clause, expr:"
+                                                + 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());
+        }
+        analyzeOrderByClause();
+        if (selectStmt.getLimit() != -1) {
+            throw new AnalysisException("The limit clause is not supported in add materialized view clause, expr:"
+                                                + " limit " + selectStmt.getLimit());
+        }
+    }
+
+    private void analyzeSelectClause() throws AnalysisException {
+        SelectList selectList = selectStmt.getSelectList();
+        if (selectList.getItems().isEmpty()) {
+            throw new AnalysisException("The materialized view must contain at least one column");
+        }
+        boolean meetAggregate = false;
+        Set<String> mvColumnNameSet = Sets.newHashSet();
+        /**
+         * 1. The columns of mv must be a single column or a aggregate column without any calculate.
+         *    Also the children of aggregate column must be a single column without any calculate.
+         *    For example:
+         *        a, sum(b) is legal.
+         *        a+b, sum(a+b) is illegal.
+         * 2. The SUM, MIN, MAX function is supported. The other function will be supported in the future.
+         * 3. The aggregate column must be declared after the single column.
+         */
+        for (SelectListItem selectListItem : selectList.getItems()) {
+            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());
+            }
+            if (selectListItem.getExpr() instanceof SlotRef) {
+                if (meetAggregate) {
+                    throw new AnalysisException("The aggregate column should be after the single column");
+                }
+                SlotRef slotRef = (SlotRef) selectListItem.getExpr();
+                String columnName = slotRef.getColumnName();
+                if (!mvColumnNameSet.add(columnName)) {
+                    ErrorReport.reportAnalysisException(ErrorCode.ERR_DUP_FIELDNAME, columnName);
+                }
+                MVColumnItem mvColumnItem = new MVColumnItem(columnName);
+                mvColumnItemList.add(mvColumnItem);
+            } else if (selectListItem.getExpr() instanceof FunctionCallExpr) {
+                FunctionCallExpr functionCallExpr = (FunctionCallExpr) selectListItem.getExpr();
+                String functionName = functionCallExpr.getFnName().getFunction();
+                // TODO(ml): support REPLACE, REPLACE_IF_NOT_NULL only for aggregate table, HLL_UNION, BITMAP_UNION
+                if (!functionName.equalsIgnoreCase("sum")
+                        && !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());
+                }
+                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)) {
+                    slotRef = (SlotRef) functionChild0.getChild(0);
+                } else {
+                    throw new AnalysisException("The children of aggregate function only support one original column. "
+                                                        + "Error function: " + functionCallExpr.toSqlImpl());
+                }
+                meetAggregate = true;
+                String columnName = slotRef.getColumnName();
+                if (!mvColumnNameSet.add(columnName)) {
+                    ErrorReport.reportAnalysisException(ErrorCode.ERR_DUP_FIELDNAME, columnName);
+                }
+                MVColumnItem mvColumnItem = new MVColumnItem(columnName);
+                mvColumnItem.setAggregationType(AggregateType.valueOf(functionName.toUpperCase()), false);
+                mvColumnItemList.add(mvColumnItem);
+            }
+        }
+        // TODO(ML): only value columns of materialized view, such as select sum(v1) from table
+    }
+
+    private void analyzeFromClause() throws AnalysisException {
+        List<TableRef> tableRefList = selectStmt.getTableRefs();
+        if (tableRefList.size() != 1) {
+            throw new AnalysisException("The materialized view only support one table in from clause.");
+        }
+        baseIndexName = tableRefList.get(0).getName().getTbl();
+    }
+
+    private void analyzeOrderByClause() throws AnalysisException {
+        if (selectStmt.getOrderByElements() == null) {
+            // supplement keys of MV columns
+            for (MVColumnItem mvColumnItem : mvColumnItemList) {
+                if (mvColumnItem.getAggregationType() != null) {
+                    break;
+                }
+                mvColumnItem.setIsKey(true);
+            }
+            return;
+        }
+
+        List<OrderByElement> orderByElements = selectStmt.getOrderByElements();
+        for (int i = 0; i < orderByElements.size(); i++) {
+            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());
+            }
+            MVColumnItem mvColumnItem = mvColumnItemList.get(i);
+            Preconditions.checkState(mvColumnItem.getAggregationType() == null);
+            SlotRef slotRef = (SlotRef) orderByElement;
+            if (!mvColumnItem.getName().equals(slotRef.getColumnName())) {
 
 Review comment:
   column's name is case insensibility, so need to use `eqaulsIgnoreCase()`

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org