You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lens.apache.org by pr...@apache.org on 2015/06/22 10:53:32 UTC

incubator-lens git commit: LENS-605 : Fix Candidate pruning wrt sourcecolumns required for joinchains

Repository: incubator-lens
Updated Branches:
  refs/heads/master 0cf6c4e2d -> 5f859118c


LENS-605 : Fix Candidate pruning wrt sourcecolumns required for joinchains


Project: http://git-wip-us.apache.org/repos/asf/incubator-lens/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-lens/commit/5f859118
Tree: http://git-wip-us.apache.org/repos/asf/incubator-lens/tree/5f859118
Diff: http://git-wip-us.apache.org/repos/asf/incubator-lens/diff/5f859118

Branch: refs/heads/master
Commit: 5f859118c2ea49cc09dd59472d266064d36ca978
Parents: 0cf6c4e
Author: Amareshwari Sriramadasu <am...@gmail.com>
Authored: Mon Jun 22 14:22:13 2015 +0530
Committer: Rajat Khandelwal <ra...@gmail.com>
Committed: Mon Jun 22 14:22:13 2015 +0530

----------------------------------------------------------------------
 .../lens/cube/parse/CandidateTableResolver.java | 124 +++++++++++++++----
 .../lens/cube/parse/CubeQueryContext.java       |  70 ++++++++++-
 .../cube/parse/DenormalizationResolver.java     |   6 +-
 .../lens/cube/parse/ExpressionResolver.java     |  27 ++--
 .../apache/lens/cube/parse/JoinResolver.java    |   4 +-
 .../cube/parse/TestDenormalizationResolver.java |  32 ++++-
 6 files changed, 218 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/5f859118/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTableResolver.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTableResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTableResolver.java
index 79d6d43..1b5c09a 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTableResolver.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTableResolver.java
@@ -23,6 +23,9 @@ import java.util.*;
 import org.apache.lens.cube.metadata.*;
 import org.apache.lens.cube.parse.CandidateTablePruneCause.CandidateTablePruneCode;
 import org.apache.lens.cube.parse.CubeQueryContext.OptionalDimCtx;
+import org.apache.lens.cube.parse.CubeQueryContext.QueriedExprColumn;
+import org.apache.lens.cube.parse.ExpressionResolver.ExprSpecContext;
+import org.apache.lens.cube.parse.ExpressionResolver.ExpressionContext;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
@@ -32,6 +35,8 @@ import org.apache.hadoop.hive.ql.ErrorMsg;
 import org.apache.hadoop.hive.ql.metadata.HiveException;
 import org.apache.hadoop.hive.ql.parse.SemanticException;
 
+import com.google.common.collect.Sets;
+
 /**
  * This resolver prunes the candidate tables for following cases
  * <p/>
@@ -493,39 +498,127 @@ class CandidateTableResolver implements ContextRewriter {
       return;
     }
     // check for source columns for denorm columns
-    Map<CandidateTable, Collection<String>> removedCandidates = new HashMap<CandidateTable, Collection<String>>();
+    Map<Dimension, Set<CandidateTable>> removedCandidates = new HashMap<Dimension, Set<CandidateTable>>();
     for (Map.Entry<Dimension, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) {
       Dimension dim = optdimEntry.getKey();
       OptionalDimCtx optdim = optdimEntry.getValue();
       Iterator<CandidateTable> iter = optdim.requiredForCandidates.iterator();
+      // remove candidates from each optional dim if the dimension is not reachable from candidate
+      Set<CandidateTable> cTableSet = Sets.newHashSet();
+      removedCandidates.put(dim, cTableSet);
       while (iter.hasNext()) {
         CandidateTable candidate = iter.next();
         boolean remove = false;
         if (cubeql.getAutoJoinCtx().getJoinPathFromColumns().get(dim) == null) {
-          LOG.info("Removing candidate" + candidate + " from requiredForCandidates of" + dim + ", as no join paths"
+          LOG.info("Removing candidate " + candidate + " from requiredForCandidates of " + dim + ", as no join paths"
             + " exist");
           remove = true;
-          removedCandidates.put(candidate, optdim.colQueried);
         } else {
           List<String> colSet = cubeql.getAutoJoinCtx().getJoinPathFromColumns().get(dim).get(candidate.getBaseTable());
           if (!checkForColumnExists(candidate, colSet)) {
-            LOG.info("Removing candidate" + candidate + " from requiredForCandidates of" + dim + ", as columns:"
+            LOG.info("Removing candidate " + candidate + " from requiredForCandidates of " + dim + ", as columns:"
               + colSet + " do not exist");
             remove = true;
-            removedCandidates.put(candidate, colSet);
           }
         }
         if (remove) {
           iter.remove();
+          cTableSet.add(candidate);
         }
       }
     }
-    Set<CandidateTable> candidatesReachableThroughRefs = new HashSet<CandidateTable>();
+    // For each ref column required in cube query
+    // remove candidates which are not reachable
+    // For example:
+    // CTable | Col1 | Col2
+    // F1 | reachable through D1 | Not reachable
+    // F2 | Reachable through D2 |  Reachable through D5
+    // F3 | Not reachable | Not reachable
+    // F4 | Not reachable | Reachable through D6
+    // F5 | Directly available | Directly available
+    // F6 | Directly available | Not reachable
+    // F3 and F4 will get pruned while iterating over col1 and F1, F6 will get pruned while iterating over col2.
+    for (Map.Entry<String, Set<Dimension>> dimColEntry : cubeql.getRefColToDim().entrySet()) {
+      Set<CandidateTable> candidatesReachableThroughRefs = new HashSet<CandidateTable>();
+      String col = dimColEntry.getKey();
+      Set<Dimension> dimSet = dimColEntry.getValue();
+      for (Dimension dim : dimSet) {
+        OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(dim);
+        candidatesReachableThroughRefs.addAll(optdim.requiredForCandidates);
+      }
+      for (Dimension dim : dimSet) {
+        for (CandidateTable candidate : removedCandidates.get(dim)) {
+          if (!candidatesReachableThroughRefs.contains(candidate)) {
+            if (candidate instanceof CandidateFact) {
+              if (cubeql.getCandidateFacts().contains(candidate)) {
+                LOG.info("Not considering fact:" + candidate + " as its required optional dims are not reachable");
+                cubeql.getCandidateFacts().remove(candidate);
+                cubeql.addFactPruningMsgs(((CandidateFact) candidate).fact,
+                  CandidateTablePruneCause.columnNotFound(col));
+              }
+            } else if (cubeql.getCandidateDimTables().containsKey(((CandidateDim) candidate).getBaseTable())) {
+              LOG.info("Not considering dimtable:" + candidate + " as its required optional dims are not reachable");
+              cubeql.getCandidateDimTables().get(((CandidateDim) candidate).getBaseTable()).remove(candidate);
+              cubeql.addDimPruningMsgs((Dimension) candidate.getBaseTable(), (CubeDimensionTable) candidate.getTable(),
+                CandidateTablePruneCause.columnNotFound(col));
+            }
+          }
+        }
+      }
+    }
+
+    // For each expression column required in cube query
+    // remove candidates in which expressions are not evaluable - for expression column all dimensions accessed in
+    // expression should be reachable from candidate.
+    // For example:
+    // CTable | Col1 | Col2
+    // F1 | evaluable through D1, D3 | Not evaluable
+    // F2 | evaluable through D2, D4 |  evaluable through D5
+    // F3 | Not evaluable | Not evaluable
+    // F4 | Not evaluable | evaluable through D6
+    // F5 | Directly available | Directly available
+    // F6 | Directly available | Not evaluable
+    for (Map.Entry<QueriedExprColumn, Set<Dimension>> exprColEntry : cubeql.getExprColToDim().entrySet()) {
+      QueriedExprColumn col = exprColEntry.getKey();
+      Set<Dimension> dimSet = exprColEntry.getValue();
+      ExpressionContext ec = cubeql.getExprCtx().getExpressionContext(col.getExprCol(), col.getAlias());
+      for (Dimension dim : dimSet) {
+        for (CandidateTable candidate : removedCandidates.get(dim)) {
+          // check if evaluable expressions of this candidate are no more evaluable because dimension is not reachable
+          // if no evaluable expressions exist, then remove the candidate
+          Iterator<ExprSpecContext> escIter = ec.getEvaluableExpressions().get(candidate).iterator();
+          while (escIter.hasNext()) {
+            ExprSpecContext esc = escIter.next();
+            if (esc.getExprDims().contains(dim)) {
+              escIter.remove();
+            }
+          }
+          if (cubeql.getExprCtx().isEvaluable(col.getExprCol(), candidate)) {
+            // candidate has other evaluable expressions
+            continue;
+          }
+          if (candidate instanceof CandidateFact) {
+            if (cubeql.getCandidateFacts().contains(candidate)) {
+              LOG.info("Not considering fact:" + candidate + " as is not reachable through any optional dim");
+              cubeql.getCandidateFacts().remove(candidate);
+              cubeql.addFactPruningMsgs(((CandidateFact) candidate).fact,
+                CandidateTablePruneCause.expressionNotEvaluable(col.getExprCol()));
+            }
+          } else if (cubeql.getCandidateDimTables().containsKey(((CandidateDim) candidate).getBaseTable())) {
+            LOG.info("Not considering dimtable:" + candidate + " as is not reachable through any optional dim");
+            cubeql.getCandidateDimTables().get(((CandidateDim) candidate).getBaseTable()).remove(candidate);
+            cubeql.addDimPruningMsgs((Dimension) candidate.getBaseTable(), (CubeDimensionTable) candidate.getTable(),
+              CandidateTablePruneCause.expressionNotEvaluable(col.getExprCol()));
+          }
+        }
+      }
+    }
+
+    // remove optional dims which are not required any more.
     Set<Dimension> tobeRemoved = new HashSet<Dimension>();
     for (Map.Entry<Dimension, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) {
       Dimension dim = optdimEntry.getKey();
       OptionalDimCtx optdim = optdimEntry.getValue();
-      candidatesReachableThroughRefs.addAll(optdim.requiredForCandidates);
       if ((!optdim.colQueried.isEmpty() && optdim.requiredForCandidates.isEmpty()) && !optdim.isRequiredInJoinChain) {
         LOG.info("Not considering optional dimension " + dim + " as all requiredForCandidates are removed");
         tobeRemoved.add(dim);
@@ -534,23 +627,6 @@ class CandidateTableResolver implements ContextRewriter {
     for (Dimension dim : tobeRemoved) {
       removeOptionalDim(cubeql, dim);
     }
-    for (CandidateTable candidate : removedCandidates.keySet()) {
-      if (!candidatesReachableThroughRefs.contains(candidate)) {
-        if (candidate instanceof CandidateFact) {
-          if (cubeql.getCandidateFacts().contains(candidate)) {
-            LOG.info("Not considering fact:" + candidate + " as is not reachable through any optional dim");
-            cubeql.getCandidateFacts().remove(candidate);
-            cubeql.addFactPruningMsgs(((CandidateFact) candidate).fact,
-              CandidateTablePruneCause.columnNotFound(removedCandidates.get(candidate)));
-          }
-        } else if (cubeql.getCandidateDimTables().containsKey(((CandidateDim) candidate).getBaseTable())) {
-          LOG.info("Not considering dimtable:" + candidate + " as is not reachable through any optional dim");
-          cubeql.getCandidateDimTables().get(((CandidateDim) candidate).getBaseTable()).remove(candidate);
-          cubeql.addDimPruningMsgs((Dimension) candidate.getBaseTable(), (CubeDimensionTable) candidate.getTable(),
-            CandidateTablePruneCause.columnNotFound(removedCandidates.get(candidate)));
-        }
-      }
-    }
   }
 
   private void resolveCandidateDimTables(CubeQueryContext cubeql) throws SemanticException {

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/5f859118/lens-cube/src/main/java/org/apache/lens/cube/parse/CubeQueryContext.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/CubeQueryContext.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/CubeQueryContext.java
index 33b1a62..57c075c 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/parse/CubeQueryContext.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/CubeQueryContext.java
@@ -45,7 +45,13 @@ import org.apache.hadoop.hive.ql.parse.*;
 import org.codehaus.jackson.map.ObjectMapper;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+
+import lombok.AllArgsConstructor;
+
+import lombok.Data;
+
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
@@ -346,11 +352,45 @@ public class CubeQueryContext implements TrackQueriedColumns {
     return partsQueried;
   }
 
+  // map of ref column in query to set of Dimension that have the column - which are added as optional dims
+  @Getter
+  private Map<String, Set<Dimension>>  refColToDim = Maps.newHashMap();
+
+  public void updateRefColDim(String col, Dimension dim) {
+    Set<Dimension> refDims = refColToDim.get(col.toLowerCase());
+    if (refDims == null) {
+      refDims = Sets.newHashSet();
+      refColToDim.put(col.toLowerCase(), refDims);
+    }
+    refDims.add(dim);
+  }
+
+  @Data
+  @AllArgsConstructor
+  static class QueriedExprColumn {
+    private String exprCol;
+    private String alias;
+  }
+  // map of expression column in query to set of Dimension that are accessed in the expression column - which are added
+  // as optional dims
+  @Getter
+  private Map<QueriedExprColumn, Set<Dimension>>  exprColToDim = Maps.newHashMap();
+
+  public void updateExprColDim(String tblAlias, String col, Dimension dim) {
+
+    QueriedExprColumn qexpr = new QueriedExprColumn(col, tblAlias);
+    Set<Dimension> exprDims = exprColToDim.get(qexpr);
+    if (exprDims == null) {
+      exprDims = Sets.newHashSet();
+      exprColToDim.put(qexpr, exprDims);
+    }
+    exprDims.add(dim);
+  }
+
   // Holds the context of optional dimension
   // A dimension is optional if it is not queried directly by the user, but is
   // required by a candidate table to get a denormalized field from reference
   // or required in a join chain
-
   @ToString
   static class OptionalDimCtx {
     OptionalDimCtx() {
@@ -361,8 +401,22 @@ public class CubeQueryContext implements TrackQueriedColumns {
     boolean isRequiredInJoinChain = false;
   }
 
-  public void addOptionalDimTable(String alias, CandidateTable candidate, boolean isRequiredInJoin, String... cols)
-    throws SemanticException {
+  public void addOptionalJoinDimTable(String alias, boolean isRequired) throws SemanticException {
+    addOptionalDimTable(alias, null, isRequired, null, false, (String[])null);
+  }
+
+  public void addOptionalExprDimTable(String dimAlias, String queriedExpr, String srcTableAlias,
+    CandidateTable candidate, String... cols) throws SemanticException {
+    addOptionalDimTable(dimAlias, candidate, false, queriedExpr, false, srcTableAlias, cols);
+  }
+
+  public void addOptionalDimTable(String alias, CandidateTable candidate, boolean isRequiredInJoin, String cubeCol,
+    boolean isRef, String... cols) throws SemanticException {
+    addOptionalDimTable(alias, candidate, isRequiredInJoin, cubeCol, true, null, cols);
+  }
+
+  private void addOptionalDimTable(String alias, CandidateTable candidate, boolean isRequiredInJoin, String cubeCol,
+    boolean isRef, String tableAlias, String... cols) throws SemanticException {
     alias = alias.toLowerCase();
     try {
       if (!addQueriedTable(alias, true)) {
@@ -380,10 +434,18 @@ public class CubeQueryContext implements TrackQueriedColumns {
         }
         optDim.requiredForCandidates.add(candidate);
       }
+      if (cubeCol != null) {
+        if (isRef) {
+          updateRefColDim(cubeCol, dim);
+        } else {
+          updateExprColDim(tableAlias, cubeCol, dim);
+        }
+      }
       if (!optDim.isRequiredInJoinChain) {
         optDim.isRequiredInJoinChain = isRequiredInJoin;
       }
-      LOG.info("Adding optional dimension:" + dim + " optDim:" + optDim);
+      LOG.info("Adding optional dimension:" + dim + " optDim:" + optDim
+        + (cubeCol == null ? "" : " for column:" + cubeCol + " isRef:" + isRef));
     } catch (HiveException e) {
       throw new SemanticException(e);
     }

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/5f859118/lens-cube/src/main/java/org/apache/lens/cube/parse/DenormalizationResolver.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/DenormalizationResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/DenormalizationResolver.java
index 3ed94fa..075cdb4 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/parse/DenormalizationResolver.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/DenormalizationResolver.java
@@ -152,11 +152,13 @@ public class DenormalizationResolver implements ContextRewriter {
             refCols.add(refer);
             // Add to optional tables
             if (refer.col.isChainedColumn()) {
-              cubeql.addOptionalDimTable(refer.col.getChainName(), table, false, refer.col.getRefColumn());
+              cubeql.addOptionalDimTable(refer.col.getChainName(), table, false, refer.col.getName(), true,
+                refer.col.getRefColumn());
 
             } else {
               for (TableReference reference : refer.col.getReferences()) {
-                cubeql.addOptionalDimTable(reference.getDestTable(), table, false, reference.getDestColumn());
+                cubeql.addOptionalDimTable(reference.getDestTable(), table, false, refer.col.getName(), true,
+                  reference.getDestColumn());
               }
             }
             return true;

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/5f859118/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java
index 8e199ea..b2997dd 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/ExpressionResolver.java
@@ -68,6 +68,7 @@ class ExpressionResolver implements ContextRewriter {
     @Getter
     private Set<ExprSpecContext> allExprs = new LinkedHashSet<ExprSpecContext>();
     private Set<CandidateTable> directlyAvailableIn = new HashSet<CandidateTable>();
+    @Getter
     private Map<CandidateTable, Set<ExprSpecContext>> evaluableExpressions =
       new HashMap<CandidateTable, Set<ExprSpecContext>>();
     private boolean hasMeasures = false;
@@ -95,7 +96,7 @@ class ExpressionResolver implements ContextRewriter {
           try {
             if (!CubeQueryContext.DEFAULT_TABLE.equalsIgnoreCase(table) && !srcAlias.equals(table)) {
               cubeql.addOptionalDimTable(table, null,
-                false, esc.getTblAliasToColumns().get(table).toArray(new String[0]));
+                false, null, false, esc.getTblAliasToColumns().get(table).toArray(new String[0]));
               esc.exprDims.add((Dimension) cubeql.getCubeTableForAlias(table));
             }
           } catch (HiveException e) {
@@ -132,7 +133,7 @@ class ExpressionResolver implements ContextRewriter {
         for (String table : esc.getTblAliasToColumns().keySet()) {
           try {
             if (!CubeQueryContext.DEFAULT_TABLE.equalsIgnoreCase(table) && !srcAlias.equals(table)) {
-              cubeql.addOptionalDimTable(table, null, false,
+              cubeql.addOptionalDimTable(table, null, false, null, false,
                 esc.getTblAliasToColumns().get(table).toArray(new String[0]));
               esc.exprDims.add((Dimension) cubeql.getCubeTableForAlias(table));
             }
@@ -171,17 +172,17 @@ class ExpressionResolver implements ContextRewriter {
       if (evalSet == null) {
         evalSet = new LinkedHashSet<ExprSpecContext>();
         evaluableExpressions.put(cTable, evalSet);
-        // add optional dimensions involved in expressions
-        for (String table : esc.getTblAliasToColumns().keySet()) {
-          try {
-            if (!CubeQueryContext.DEFAULT_TABLE.equalsIgnoreCase(table) && !srcAlias.equals(table)) {
-              cubeql.addOptionalDimTable(table, cTable,
-                false, esc.getTblAliasToColumns().get(table).toArray(new String[0]));
-              esc.exprDims.add((Dimension) cubeql.getCubeTableForAlias(table));
-            }
-          } catch (HiveException e) {
-            throw new SemanticException(e);
+      }
+      // add optional dimensions involved in expressions
+      for (String table : esc.getTblAliasToColumns().keySet()) {
+        try {
+          if (!CubeQueryContext.DEFAULT_TABLE.equalsIgnoreCase(table) && !srcAlias.equals(table)) {
+            cubeql.addOptionalExprDimTable(table, exprCol.getName(), srcAlias, cTable,
+              esc.getTblAliasToColumns().get(table).toArray(new String[0]));
+            esc.exprDims.add((Dimension) cubeql.getCubeTableForAlias(table));
           }
+        } catch (HiveException e) {
+          throw new SemanticException(e);
         }
       }
       evalSet.add(esc);
@@ -210,6 +211,7 @@ class ExpressionResolver implements ContextRewriter {
     private Set<ExprSpec> exprSpecs = new LinkedHashSet<ExprSpec>();
     @Getter
     private ASTNode finalAST;
+    @Getter
     private Set<Dimension> exprDims = new HashSet<Dimension>();
     // for each expression store alias to columns queried
     @Getter
@@ -367,6 +369,7 @@ class ExpressionResolver implements ContextRewriter {
       }
       for (ExprSpecContext esc : ec.allExprs) {
         if (esc.getTblAliasToColumns().get(alias) == null) {
+          log.debug("{} = {} is evaluable in {}", expr, esc, cTable);
           ec.addEvaluable(cubeql, cTable, esc);
         } else {
           Set<String> columns = esc.getTblAliasToColumns().get(alias);

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/5f859118/lens-cube/src/main/java/org/apache/lens/cube/parse/JoinResolver.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/JoinResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/JoinResolver.java
index a760599..97c7a46 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/parse/JoinResolver.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/JoinResolver.java
@@ -1016,7 +1016,7 @@ class JoinResolver implements ContextRewriter {
 
     for (JoinChain chain : cubeql.getJoinchains().values()) {
       for (String dimName : chain.getIntermediateDimensions()) {
-        cubeql.addOptionalDimTable(dimName, null, true, null);
+        cubeql.addOptionalJoinDimTable(dimName, true);
       }
     }
 
@@ -1122,7 +1122,7 @@ class JoinResolver implements ContextRewriter {
       for (TableRelationship rel : joinPath.getEdges()) {
         // Add the joined tables to the queries table sets so that they are
         // resolved in candidate resolver
-        cubeql.addOptionalDimTable(rel.getToTable().getName(), null, required, null);
+        cubeql.addOptionalJoinDimTable(rel.getToTable().getName(), required);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/5f859118/lens-cube/src/test/java/org/apache/lens/cube/parse/TestDenormalizationResolver.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/test/java/org/apache/lens/cube/parse/TestDenormalizationResolver.java b/lens-cube/src/test/java/org/apache/lens/cube/parse/TestDenormalizationResolver.java
index e615ccc..8c3c219 100644
--- a/lens-cube/src/test/java/org/apache/lens/cube/parse/TestDenormalizationResolver.java
+++ b/lens-cube/src/test/java/org/apache/lens/cube/parse/TestDenormalizationResolver.java
@@ -23,7 +23,9 @@ import static org.apache.lens.cube.parse.CubeTestSetup.*;
 
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.lens.cube.parse.CandidateTablePruneCause.CandidateTablePruneCode;
 import org.apache.lens.server.api.error.LensException;
@@ -164,7 +166,7 @@ public class TestDenormalizationResolver extends TestQueryRewrite {
               }))
           );
           put("summary1,cheapfact,testfactmonthly,testfact2,testfact",
-            Arrays.asList(CandidateTablePruneCause.columnNotFound("dim2big1", "dim2")));
+            Arrays.asList(CandidateTablePruneCause.columnNotFound("dim2big2")));
         }
       }
     ));
@@ -225,6 +227,34 @@ public class TestDenormalizationResolver extends TestQueryRewrite {
   }
 
   @Test
+  public void testCubeQueryWithTwoRefCols() throws Exception {
+    Configuration tConf = new Configuration(conf);
+    tConf.set(CubeQueryConfUtil.DRIVER_SUPPORTED_STORAGES, "");
+    CubeQueryContext cubeql = rewriteCtx("select dim2, test_time_dim2 from testcube where " + TWO_DAYS_RANGE, tConf);
+    Set<String> candidateFacts = new HashSet<String>();
+    for (CandidateFact cfact : cubeql.getCandidateFacts()) {
+      candidateFacts.add(cfact.getName().toLowerCase());
+    }
+    // testfact contains test_time_dim_hour_id, but not dim2 - it should have been removed.
+    Assert.assertFalse(candidateFacts.contains("testfact"));
+    // summary2 contains dim2, but not test_time_dim2 - it should have been removed.
+    Assert.assertFalse(candidateFacts.contains("summary2"));
+  }
+
+  @Test
+  public void testDimensionQueryWithTwoRefCols() throws Exception {
+    Configuration tConf = new Configuration(conf);
+    tConf.set(CubeQueryConfUtil.DRIVER_SUPPORTED_STORAGES, "");
+    CubeQueryContext cubeql = rewriteCtx("select citydim.zipcode, citydim.statename from" + " citydim", tConf);
+    Set<String> candidateDims = new HashSet<String>();
+    for (CandidateDim cdim : cubeql.getCandidateDims().get(cubeql.getMetastoreClient().getDimension("citydim"))) {
+      candidateDims.add(cdim.getName());
+    }
+    // city_table2 contains stateid, but not zipcode - it should have been removed.
+    Assert.assertFalse(candidateDims.contains("city_table2"));
+  }
+
+  @Test
   public void testDimensionQueryWithExpressionHavingDenormColumn() throws Exception {
     String hqlQuery = rewrite("select citydim.name, citydim.citystate from" + " citydim", conf);
     String joinExpr =