Repository: kylin
Updated Branches:
  refs/heads/2.0.x 0f68486f2 -> ad67fd763


KYLIN-2631 Seek to next model when no cube in current model satisfies query


Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/ad67fd76
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/ad67fd76
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/ad67fd76

Branch: refs/heads/2.0.x
Commit: ad67fd763237306ddba0d0946ab024fcdb7ab604
Parents: 0f68486
Author: Hongbin Ma <mahong...@apache.org>
Authored: Fri May 19 21:12:26 2017 +0800
Committer: Hongbin Ma <mahong...@apache.org>
Committed: Fri May 19 21:12:34 2017 +0800

----------------------------------------------------------------------
 .../apache/kylin/metadata/model/TblColRef.java  |  43 ++--
 .../kylin/metadata/realization/SQLDigest.java   |   2 +
 .../gtrecord/GTCubeStorageQueryBase.java        |   3 -
 .../kylin/query/relnode/OLAPTableScan.java      |  13 +-
 .../relnode/OLAPToEnumerableConverter.java      |  11 +-
 .../kylin/query/routing/ModelChooser.java       | 219 ------------------
 .../apache/kylin/query/routing/QueryRouter.java |   2 +-
 .../kylin/query/routing/RealizationChooser.java | 224 +++++++++++++++++++
 8 files changed, 271 insertions(+), 246 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
----------------------------------------------------------------------
diff --git 
a/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java 
b/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
index c74f4e8..1410841 100644
--- a/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
@@ -56,7 +56,7 @@ public class TblColRef implements Serializable {
     public static TblColRef newInnerColumn(String columnName, 
InnerDataTypeEnum dataType) {
         return newInnerColumn(columnName, dataType, null);
     }
-    
+
     // used by projection rewrite, see OLAPProjectRel
     public static TblColRef newInnerColumn(String columnName, 
InnerDataTypeEnum dataType, String parserDescription) {
         ColumnDesc column = new ColumnDesc();
@@ -68,27 +68,30 @@ public class TblColRef implements Serializable {
         colRef.parserDescription = parserDescription;
         return colRef;
     }
-    
+
     private static final DataModelDesc UNKNOWN_MODEL = new DataModelDesc();
     static {
         UNKNOWN_MODEL.setName("UNKNOWN_MODEL");
     }
-    
+
     public static TableRef tableForUnknownModel(String tempTableAlias, 
TableDesc table) {
         return new TableRef(UNKNOWN_MODEL, tempTableAlias, table);
     }
-    
+
     public static TblColRef columnForUnknownModel(TableRef table, ColumnDesc 
colDesc) {
         checkArgument(table.getModel() == UNKNOWN_MODEL);
         return new TblColRef(table, colDesc);
     }
-    
+
     public static void fixUnknownModel(DataModelDesc model, String alias, 
TblColRef col) {
         checkArgument(col.table.getModel() == UNKNOWN_MODEL || 
col.table.getModel() == model);
         TableRef tableRef = model.findTable(alias);
         checkArgument(tableRef.getTableDesc() == col.column.getTable());
-        col.table = tableRef;
-        col.identity = null;
+        col.fixTableRef(tableRef);
+    }
+
+    public static void unfixUnknownModel(TblColRef col) {
+        col.unfixTableRef();
     }
 
     // for test mainly
@@ -101,10 +104,11 @@ public class TblColRef implements Serializable {
         desc.init(table);
         return new TblColRef(desc);
     }
-    
+
     // 
============================================================================
 
     private TableRef table;
+    private TableRef backupTable;// only used in fixTableRef()
     private ColumnDesc column;
     private String identity;
     private String parserDescription;
@@ -112,13 +116,24 @@ public class TblColRef implements Serializable {
     TblColRef(ColumnDesc column) {
         this.column = column;
     }
-    
+
     TblColRef(TableRef table, ColumnDesc column) {
         checkArgument(table.getTableDesc() == column.getTable());
         this.table = table;
         this.column = column;
     }
-    
+
+    public void fixTableRef(TableRef tableRef) {
+        this.backupTable = this.table;
+        this.table = tableRef;
+        this.identity = null;
+    }
+
+    public void unfixTableRef() {
+        this.table = backupTable;
+        this.identity = null;
+    }
+
     public ColumnDesc getColumnDesc() {
         return column;
     }
@@ -130,15 +145,15 @@ public class TblColRef implements Serializable {
     public TableRef getTableRef() {
         return table;
     }
-    
+
     public boolean isQualified() {
         return table != null;
     }
-    
+
     public String getTableAlias() {
         return table != null ? table.getAlias() : "UNKNOWN_ALIAS";
     }
-    
+
     public String getTable() {
         if (column.getTable() == null) {
             return null;
@@ -209,7 +224,7 @@ public class TblColRef implements Serializable {
     public String toString() {
         if (isInnerColumn() && parserDescription != null)
             return parserDescription;
-        
+
         String alias = table == null ? "UNKNOWN_MODEL" : table.getAlias();
         String tableName = column.getTable() == null ? "NULL" : 
column.getTable().getName();
         String tableIdentity = column.getTable() == null ? "NULL" : 
column.getTable().getIdentity();

http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/core-metadata/src/main/java/org/apache/kylin/metadata/realization/SQLDigest.java
----------------------------------------------------------------------
diff --git 
a/core-metadata/src/main/java/org/apache/kylin/metadata/realization/SQLDigest.java
 
b/core-metadata/src/main/java/org/apache/kylin/metadata/realization/SQLDigest.java
index 03ff3ff..9ce65bb 100644
--- 
a/core-metadata/src/main/java/org/apache/kylin/metadata/realization/SQLDigest.java
+++ 
b/core-metadata/src/main/java/org/apache/kylin/metadata/realization/SQLDigest.java
@@ -96,6 +96,8 @@ public class SQLDigest {
         this.sortOrders = sortOrders;
         this.isRawQuery = isRawQuery();
         this.limitPrecedesAggr = limitPrecedesAggr;
+        
+        this.includeSubqueryJoinParticipants();
     }
 
     private boolean isRawQuery() {

http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/core-storage/src/main/java/org/apache/kylin/storage/gtrecord/GTCubeStorageQueryBase.java
----------------------------------------------------------------------
diff --git 
a/core-storage/src/main/java/org/apache/kylin/storage/gtrecord/GTCubeStorageQueryBase.java
 
b/core-storage/src/main/java/org/apache/kylin/storage/gtrecord/GTCubeStorageQueryBase.java
index d050b4b..6c2ff5d 100644
--- 
a/core-storage/src/main/java/org/apache/kylin/storage/gtrecord/GTCubeStorageQueryBase.java
+++ 
b/core-storage/src/main/java/org/apache/kylin/storage/gtrecord/GTCubeStorageQueryBase.java
@@ -99,9 +99,6 @@ public abstract class GTCubeStorageQueryBase implements 
IStorageQuery {
     protected GTCubeStorageQueryRequest getStorageQueryRequest(StorageContext 
context, SQLDigest sqlDigest, TupleInfo returnTupleInfo) {
         context.setStorageQuery(this);
 
-        //deal with participant columns in subquery join
-        sqlDigest.includeSubqueryJoinParticipants();
-
         //cope with queries with no aggregations
         RawQueryLastHacker.hackNoAggregations(sqlDigest, cubeDesc, 
returnTupleInfo);
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/query/src/main/java/org/apache/kylin/query/relnode/OLAPTableScan.java
----------------------------------------------------------------------
diff --git 
a/query/src/main/java/org/apache/kylin/query/relnode/OLAPTableScan.java 
b/query/src/main/java/org/apache/kylin/query/relnode/OLAPTableScan.java
index a424818..b583291 100644
--- a/query/src/main/java/org/apache/kylin/query/relnode/OLAPTableScan.java
+++ b/query/src/main/java/org/apache/kylin/query/relnode/OLAPTableScan.java
@@ -87,6 +87,7 @@ public class OLAPTableScan extends TableScan implements 
OLAPRel, EnumerableRel {
     private final String tableName;
     private final int[] fields;
     private String alias;
+    private String backupAlias;
     private ColumnRowType columnRowType;
     private OLAPContext context;
 
@@ -212,7 +213,7 @@ public class OLAPTableScan extends TableScan implements 
OLAPRel, EnumerableRel {
         if (implementor.getContext() == null || !(implementor.getParentNode() 
instanceof OLAPJoinRel) || implementor.isNewOLAPContextRequired()) {
             implementor.allocateContext();
         }
-        
+
         columnRowType = buildColumnRowType();
         context = implementor.getContext();
         context.allTableScans.add(this);
@@ -262,9 +263,19 @@ public class OLAPTableScan extends TableScan implements 
OLAPRel, EnumerableRel {
         for (TblColRef col : columnRowType.getAllColumns()) {
             TblColRef.fixUnknownModel(model, newAlias, col);
         }
+
+        this.backupAlias = this.alias;
         this.alias = newAlias;
     }
 
+    public void unfixColumnRowTypeWithModel() {
+        this.alias = this.backupAlias;
+
+        for (TblColRef col : columnRowType.getAllColumns()) {
+            TblColRef.unfixUnknownModel(col);
+        }
+    }
+
     @Override
     public EnumerableRel implementEnumerable(List<EnumerableRel> inputs) {
         return this;

http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/query/src/main/java/org/apache/kylin/query/relnode/OLAPToEnumerableConverter.java
----------------------------------------------------------------------
diff --git 
a/query/src/main/java/org/apache/kylin/query/relnode/OLAPToEnumerableConverter.java
 
b/query/src/main/java/org/apache/kylin/query/relnode/OLAPToEnumerableConverter.java
index 4a49d04..7ac86b2 100644
--- 
a/query/src/main/java/org/apache/kylin/query/relnode/OLAPToEnumerableConverter.java
+++ 
b/query/src/main/java/org/apache/kylin/query/relnode/OLAPToEnumerableConverter.java
@@ -18,7 +18,6 @@
 
 package org.apache.kylin.query.relnode;
 
-import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Set;
 
@@ -45,9 +44,7 @@ import org.apache.kylin.common.util.ClassUtil;
 import org.apache.kylin.metadata.filter.ColumnTupleFilter;
 import org.apache.kylin.metadata.filter.TupleFilter;
 import org.apache.kylin.metadata.model.TblColRef;
-import org.apache.kylin.metadata.realization.IRealization;
-import org.apache.kylin.query.routing.ModelChooser;
-import org.apache.kylin.query.routing.QueryRouter;
+import org.apache.kylin.query.routing.RealizationChooser;
 import org.apache.kylin.query.schema.OLAPTable;
 
 import com.google.common.collect.Lists;
@@ -79,19 +76,17 @@ public class OLAPToEnumerableConverter extends 
ConverterImpl implements Enumerab
             System.out.println("EXECUTION PLAN BEFORE REWRITE");
             System.out.println(dumpPlan);
         }
-        
+
         // post-order travel children
         OLAPRel.OLAPImplementor olapImplementor = new 
OLAPRel.OLAPImplementor();
         olapImplementor.visitChild(getInput(), this);
 
         // identify model
         List<OLAPContext> contexts = listContextsHavingScan();
-        IdentityHashMap<OLAPContext, Set<IRealization>> candidates = 
ModelChooser.selectModel(contexts);
+        RealizationChooser.selectRealization(contexts);
 
         // identify realization for each context
         for (OLAPContext context : contexts) {
-            IRealization realization = QueryRouter.selectRealization(context, 
candidates.get(context));
-            context.realization = realization;
             doAccessControl(context);
         }
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/query/src/main/java/org/apache/kylin/query/routing/ModelChooser.java
----------------------------------------------------------------------
diff --git 
a/query/src/main/java/org/apache/kylin/query/routing/ModelChooser.java 
b/query/src/main/java/org/apache/kylin/query/routing/ModelChooser.java
deleted file mode 100644
index f842345..0000000
--- a/query/src/main/java/org/apache/kylin/query/routing/ModelChooser.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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.kylin.query.routing;
-
-import java.util.Comparator;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.apache.kylin.common.KylinConfig;
-import org.apache.kylin.cube.CubeInstance;
-import org.apache.kylin.metadata.model.ColumnDesc;
-import org.apache.kylin.metadata.model.DataModelDesc;
-import org.apache.kylin.metadata.model.JoinDesc;
-import org.apache.kylin.metadata.model.JoinTableDesc;
-import org.apache.kylin.metadata.model.JoinsTree;
-import org.apache.kylin.metadata.model.TableRef;
-import org.apache.kylin.metadata.model.TblColRef;
-import org.apache.kylin.metadata.project.ProjectManager;
-import org.apache.kylin.metadata.realization.IRealization;
-import org.apache.kylin.query.relnode.OLAPContext;
-import org.apache.kylin.query.relnode.OLAPTableScan;
-import org.apache.kylin.query.routing.rules.RemoveBlackoutRealizationsRule;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-public class ModelChooser {
-
-    // select models for given contexts, return realization candidates for 
each context
-    public static IdentityHashMap<OLAPContext, Set<IRealization>> 
selectModel(List<OLAPContext> contexts) {
-
-        IdentityHashMap<OLAPContext, Set<IRealization>> candidates = new 
IdentityHashMap<>();
-
-        // attempt one model for all contexts
-        //        Set<IRealization> reals = attemptSelectModel(contexts);
-        //        if (reals != null) {
-        //            for (OLAPContext ctx : contexts) {
-        //                candidates.put(ctx, reals);
-        //            }
-        //            return candidates;
-        //        }
-
-        // try different model for different context
-        for (OLAPContext ctx : contexts) {
-            Set<IRealization> reals = 
attemptSelectModel(ImmutableList.of(ctx));
-            if (reals == null)
-                throw new NoRealizationFoundException("No model found for" + 
toErrorMsg(ctx));
-
-            candidates.put(ctx, reals);
-        }
-        return candidates;
-    }
-
-    private static Set<IRealization> attemptSelectModel(List<OLAPContext> 
contexts) {
-        Map<DataModelDesc, Set<IRealization>> modelMap = 
makeOrderedModelMap(contexts);
-
-        for (DataModelDesc model : modelMap.keySet()) {
-            Map<String, String> aliasMap = matches(model, contexts);
-            if (aliasMap != null) {
-                for (OLAPContext ctx : contexts)
-                    fixModel(ctx, model, aliasMap);
-                return modelMap.get(model);
-            }
-        }
-        return null;
-    }
-
-    private static String toErrorMsg(OLAPContext ctx) {
-        StringBuilder buf = new StringBuilder();
-        buf.append(ctx.firstTableScan);
-        for (JoinDesc join : ctx.joins)
-            buf.append(", ").append(join);
-        return buf.toString();
-    }
-
-    private static Map<String, String> matches(DataModelDesc model, 
List<OLAPContext> contexts) {
-        Map<String, String> result = Maps.newHashMap();
-
-        for (OLAPContext ctx : contexts) {
-            TableRef firstTable = ctx.firstTableScan.getTableRef();
-
-            Map<String, String> matchUp = null;
-
-            if (ctx.joins.isEmpty() && 
model.isLookupTable(firstTable.getTableIdentity())) {
-                // one lookup table
-                String modelAlias = 
model.findFirstTable(firstTable.getTableIdentity()).getAlias();
-                matchUp = ImmutableMap.of(firstTable.getAlias(), modelAlias);
-            } else if (ctx.joins.size() != ctx.allTableScans.size() - 1) {
-                // has hanging tables
-                throw new NoRealizationFoundException("Please adjust the 
sequence of join tables and put subquery or temporary table after lookup 
tables. " + toErrorMsg(ctx));
-            } else {
-                // normal big joins
-                if (ctx.joinsTree == null) {
-                    ctx.joinsTree = new JoinsTree(firstTable, ctx.joins);
-                }
-                matchUp = ctx.joinsTree.matches(model.getJoinsTree(), result);
-            }
-
-            if (matchUp == null)
-                return null;
-
-            result.putAll(matchUp);
-        }
-        return result;
-    }
-
-    private static Map<DataModelDesc, Set<IRealization>> 
makeOrderedModelMap(List<OLAPContext> contexts) {
-        // the first context, which is the top most context, contains all 
columns from all contexts
-        OLAPContext first = contexts.get(0);
-        KylinConfig kylinConfig = first.olapSchema.getConfig();
-        String projectName = first.olapSchema.getProjectName();
-        String factTableName = 
first.firstTableScan.getOlapTable().getTableName();
-        Set<IRealization> realizations = 
ProjectManager.getInstance(kylinConfig).getRealizationsByTable(projectName, 
factTableName);
-
-        final Map<DataModelDesc, Set<IRealization>> models = Maps.newHashMap();
-        final Map<DataModelDesc, RealizationCost> costs = Maps.newHashMap();
-        for (IRealization real : realizations) {
-            if (real.isReady() == false)
-                continue;
-            if (containsAll(real.getAllColumnDescs(), first.allColumns) == 
false)
-                continue;
-            if (RemoveBlackoutRealizationsRule.accept(real) == false)
-                continue;
-
-            RealizationCost cost = new RealizationCost(real);
-            DataModelDesc m = real.getModel();
-            Set<IRealization> set = models.get(m);
-            if (set == null) {
-                set = Sets.newHashSet();
-                set.add(real);
-                models.put(m, set);
-                costs.put(m, cost);
-            } else {
-                set.add(real);
-                RealizationCost curCost = costs.get(m);
-                if (cost.compareTo(curCost) < 0)
-                    costs.put(m, cost);
-            }
-        }
-
-        // order model by cheapest realization cost
-        TreeMap<DataModelDesc, Set<IRealization>> result = Maps.newTreeMap(new 
Comparator<DataModelDesc>() {
-            @Override
-            public int compare(DataModelDesc o1, DataModelDesc o2) {
-                RealizationCost c1 = costs.get(o1);
-                RealizationCost c2 = costs.get(o2);
-                int comp = c1.compareTo(c2);
-                if (comp == 0)
-                    comp = o1.getName().compareTo(o2.getName());
-                return comp;
-            }
-        });
-        result.putAll(models);
-
-        return result;
-    }
-
-    private static boolean containsAll(Set<ColumnDesc> allColumnDescs, 
Set<TblColRef> allColumns) {
-        for (TblColRef col : allColumns) {
-            if (allColumnDescs.contains(col.getColumnDesc()) == false)
-                return false;
-        }
-        return true;
-    }
-
-    private static void fixModel(OLAPContext context, DataModelDesc model, 
Map<String, String> aliasMap) {
-        for (OLAPTableScan tableScan : context.allTableScans) {
-            tableScan.fixColumnRowTypeWithModel(model, aliasMap);
-        }
-    }
-
-    private static class RealizationCost implements 
Comparable<RealizationCost> {
-        final public int priority;
-        final public int cost;
-
-        public RealizationCost(IRealization real) {
-            // ref Candidate.PRIORITIES
-            this.priority = Candidate.PRIORITIES.get(real.getType());
-
-            // ref CubeInstance.getCost()
-            int c = real.getAllDimensions().size() * 
CubeInstance.COST_WEIGHT_DIMENSION + real.getMeasures().size() * 
CubeInstance.COST_WEIGHT_MEASURE;
-            for (JoinTableDesc join : real.getModel().getJoinTables()) {
-                if (join.getJoin().isInnerJoin())
-                    c += CubeInstance.COST_WEIGHT_INNER_JOIN;
-            }
-            this.cost = c;
-        }
-
-        @Override
-        public int compareTo(RealizationCost o) {
-            int comp = this.priority - o.priority;
-            if (comp != 0)
-                return comp;
-            else
-                return this.cost - o.cost;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/query/src/main/java/org/apache/kylin/query/routing/QueryRouter.java
----------------------------------------------------------------------
diff --git 
a/query/src/main/java/org/apache/kylin/query/routing/QueryRouter.java 
b/query/src/main/java/org/apache/kylin/query/routing/QueryRouter.java
index 2975cf7..18db4ac 100644
--- a/query/src/main/java/org/apache/kylin/query/routing/QueryRouter.java
+++ b/query/src/main/java/org/apache/kylin/query/routing/QueryRouter.java
@@ -59,7 +59,7 @@ public class QueryRouter {
         RoutingRule.applyRules(candidates);
 
         if (candidates.size() == 0) {
-            throw new NoRealizationFoundException("Can't find any realization. 
Please confirm with providers. SQL digest: " + sqlDigest.toString());
+            return null;
         }
 
         Candidate chosen = candidates.get(0);

http://git-wip-us.apache.org/repos/asf/kylin/blob/ad67fd76/query/src/main/java/org/apache/kylin/query/routing/RealizationChooser.java
----------------------------------------------------------------------
diff --git 
a/query/src/main/java/org/apache/kylin/query/routing/RealizationChooser.java 
b/query/src/main/java/org/apache/kylin/query/routing/RealizationChooser.java
new file mode 100644
index 0000000..400043b
--- /dev/null
+++ b/query/src/main/java/org/apache/kylin/query/routing/RealizationChooser.java
@@ -0,0 +1,224 @@
+/*
+ * 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.kylin.query.routing;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.kylin.common.KylinConfig;
+import org.apache.kylin.cube.CubeInstance;
+import org.apache.kylin.metadata.model.ColumnDesc;
+import org.apache.kylin.metadata.model.DataModelDesc;
+import org.apache.kylin.metadata.model.JoinDesc;
+import org.apache.kylin.metadata.model.JoinTableDesc;
+import org.apache.kylin.metadata.model.JoinsTree;
+import org.apache.kylin.metadata.model.TableRef;
+import org.apache.kylin.metadata.model.TblColRef;
+import org.apache.kylin.metadata.project.ProjectManager;
+import org.apache.kylin.metadata.realization.IRealization;
+import org.apache.kylin.query.relnode.OLAPContext;
+import org.apache.kylin.query.relnode.OLAPTableScan;
+import org.apache.kylin.query.routing.rules.RemoveBlackoutRealizationsRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+public class RealizationChooser {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(RealizationChooser.class);
+
+    // select models for given contexts, return realization candidates for 
each context
+    public static void selectRealization(List<OLAPContext> contexts) {
+        // try different model for different context
+        for (OLAPContext ctx : contexts) {
+            attemptSelectRealization(ctx);
+            Preconditions.checkNotNull(ctx.realization);
+        }
+    }
+
+    private static void attemptSelectRealization(OLAPContext context) {
+        Map<DataModelDesc, Set<IRealization>> modelMap = 
makeOrderedModelMap(context);
+
+        if (modelMap.size() == 0) {
+            throw new NoRealizationFoundException("No model found for" + 
toErrorMsg(context));
+        }
+
+        for (DataModelDesc model : modelMap.keySet()) {
+            Map<String, String> aliasMap = matches(model, context);
+            if (aliasMap != null) {
+                fixModel(context, model, aliasMap);
+
+                IRealization realization = 
QueryRouter.selectRealization(context, modelMap.get(model));
+                if (realization == null) {
+                    logger.info("Give up on model {} because no suitable 
realization is found");
+                    unfixModel(context);
+                    continue;
+                }
+
+                context.realization = realization;
+                return;
+            }
+        }
+
+        throw new NoRealizationFoundException("No realization found for " + 
toErrorMsg(context));
+
+    }
+
+    private static String toErrorMsg(OLAPContext ctx) {
+        StringBuilder buf = new StringBuilder();
+        buf.append(ctx.firstTableScan);
+        for (JoinDesc join : ctx.joins)
+            buf.append(", ").append(join);
+        return buf.toString();
+    }
+
+    private static Map<String, String> matches(DataModelDesc model, 
OLAPContext ctx) {
+        Map<String, String> result = Maps.newHashMap();
+
+        TableRef firstTable = ctx.firstTableScan.getTableRef();
+
+        Map<String, String> matchUp = null;
+
+        if (ctx.joins.isEmpty() && 
model.isLookupTable(firstTable.getTableIdentity())) {
+            // one lookup table
+            String modelAlias = 
model.findFirstTable(firstTable.getTableIdentity()).getAlias();
+            matchUp = ImmutableMap.of(firstTable.getAlias(), modelAlias);
+        } else if (ctx.joins.size() != ctx.allTableScans.size() - 1) {
+            // has hanging tables
+            throw new IllegalStateException("Please adjust the sequence of 
join tables. " + toErrorMsg(ctx));
+        } else {
+            // normal big joins
+            if (ctx.joinsTree == null) {
+                ctx.joinsTree = new JoinsTree(firstTable, ctx.joins);
+            }
+            matchUp = ctx.joinsTree.matches(model.getJoinsTree(), result);
+        }
+
+        if (matchUp == null)
+            return null;
+
+        result.putAll(matchUp);
+
+        return result;
+    }
+
+    private static Map<DataModelDesc, Set<IRealization>> 
makeOrderedModelMap(OLAPContext context) {
+        OLAPContext first = context;
+        KylinConfig kylinConfig = first.olapSchema.getConfig();
+        String projectName = first.olapSchema.getProjectName();
+        String factTableName = 
first.firstTableScan.getOlapTable().getTableName();
+        Set<IRealization> realizations = 
ProjectManager.getInstance(kylinConfig).getRealizationsByTable(projectName, 
factTableName);
+
+        final Map<DataModelDesc, Set<IRealization>> models = Maps.newHashMap();
+        final Map<DataModelDesc, RealizationCost> costs = Maps.newHashMap();
+        for (IRealization real : realizations) {
+            if (real.isReady() == false)
+                continue;
+            if (containsAll(real.getAllColumnDescs(), first.allColumns) == 
false)
+                continue;
+            if (RemoveBlackoutRealizationsRule.accept(real) == false)
+                continue;
+
+            RealizationCost cost = new RealizationCost(real);
+            DataModelDesc m = real.getModel();
+            Set<IRealization> set = models.get(m);
+            if (set == null) {
+                set = Sets.newHashSet();
+                set.add(real);
+                models.put(m, set);
+                costs.put(m, cost);
+            } else {
+                set.add(real);
+                RealizationCost curCost = costs.get(m);
+                if (cost.compareTo(curCost) < 0)
+                    costs.put(m, cost);
+            }
+        }
+
+        // order model by cheapest realization cost
+        TreeMap<DataModelDesc, Set<IRealization>> result = Maps.newTreeMap(new 
Comparator<DataModelDesc>() {
+            @Override
+            public int compare(DataModelDesc o1, DataModelDesc o2) {
+                RealizationCost c1 = costs.get(o1);
+                RealizationCost c2 = costs.get(o2);
+                int comp = c1.compareTo(c2);
+                if (comp == 0)
+                    comp = o1.getName().compareTo(o2.getName());
+                return comp;
+            }
+        });
+        result.putAll(models);
+
+        return result;
+    }
+
+    private static boolean containsAll(Set<ColumnDesc> allColumnDescs, 
Set<TblColRef> allColumns) {
+        for (TblColRef col : allColumns) {
+            if (allColumnDescs.contains(col.getColumnDesc()) == false)
+                return false;
+        }
+        return true;
+    }
+
+    private static void fixModel(OLAPContext context, DataModelDesc model, 
Map<String, String> aliasMap) {
+        for (OLAPTableScan tableScan : context.allTableScans) {
+            tableScan.fixColumnRowTypeWithModel(model, aliasMap);
+        }
+    }
+
+    private static void unfixModel(OLAPContext context) {
+        for (OLAPTableScan tableScan : context.allTableScans) {
+            tableScan.unfixColumnRowTypeWithModel();
+        }
+    }
+
+    private static class RealizationCost implements 
Comparable<RealizationCost> {
+        final public int priority;
+        final public int cost;
+
+        public RealizationCost(IRealization real) {
+            // ref Candidate.PRIORITIES
+            this.priority = Candidate.PRIORITIES.get(real.getType());
+
+            // ref CubeInstance.getCost()
+            int c = real.getAllDimensions().size() * 
CubeInstance.COST_WEIGHT_DIMENSION + real.getMeasures().size() * 
CubeInstance.COST_WEIGHT_MEASURE;
+            for (JoinTableDesc join : real.getModel().getJoinTables()) {
+                if (join.getJoin().isInnerJoin())
+                    c += CubeInstance.COST_WEIGHT_INNER_JOIN;
+            }
+            this.cost = c;
+        }
+
+        @Override
+        public int compareTo(RealizationCost o) {
+            int comp = this.priority - o.priority;
+            if (comp != 0)
+                return comp;
+            else
+                return this.cost - o.cost;
+        }
+    }
+}

Reply via email to