Repository: lens Updated Branches: refs/heads/master 9c03c76e6 -> c7451f8e8
LENS-917 : Fixs table pruning for multiple chains for same destination table Project: http://git-wip-us.apache.org/repos/asf/lens/repo Commit: http://git-wip-us.apache.org/repos/asf/lens/commit/c7451f8e Tree: http://git-wip-us.apache.org/repos/asf/lens/tree/c7451f8e Diff: http://git-wip-us.apache.org/repos/asf/lens/diff/c7451f8e Branch: refs/heads/master Commit: c7451f8e8e8f429fc55458b03dbb10a2b7428be9 Parents: 9c03c76 Author: Amareshwari Sriramadasu <amareshw...@apache.org> Authored: Tue Jan 12 11:09:24 2016 +0530 Committer: Amareshwari Sriramadasu <amareshw...@apache.org> Committed: Tue Jan 12 11:09:24 2016 +0530 ---------------------------------------------------------------------- .../org/apache/lens/cube/parse/Aliased.java | 4 + .../lens/cube/parse/CandidateTableResolver.java | 150 +++++++++---------- .../lens/cube/parse/CubeQueryContext.java | 43 +++--- .../lens/cube/parse/ExpressionResolver.java | 3 + .../lens/cube/parse/StorageTableResolver.java | 4 +- .../lens/cube/parse/join/AutoJoinContext.java | 12 +- .../apache/lens/cube/parse/CubeTestSetup.java | 28 ++++ .../cube/parse/TestDenormalizationResolver.java | 16 +- 8 files changed, 152 insertions(+), 108 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/lens-cube/src/main/java/org/apache/lens/cube/parse/Aliased.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/Aliased.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/Aliased.java index 56fe9fc..160a9c6 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/Aliased.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/Aliased.java @@ -36,4 +36,8 @@ public class Aliased<T extends Named> { public static <K extends Named> Aliased<K> create(K obj, String alias) { return new Aliased<K>(obj, alias); } + + public String getName() { + return object.getName(); + } } http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/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 38ff5a4..00ccf36 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 @@ -67,9 +67,9 @@ class CandidateTableResolver implements ContextRewriter { checkForQueriedColumns = false; } else { // populate optional tables - for (Dimension dim : cubeql.getOptionalDimensions()) { + for (Aliased<Dimension> dim : cubeql.getOptionalDimensions()) { log.info("Populating optional dim:{}", dim); - populateDimTables(dim, cubeql, true); + populateDimTables(dim.getObject(), cubeql, true); } if (cubeql.getAutoJoinCtx() != null) { // Before checking for candidate table columns, prune join paths containing non existing columns @@ -117,7 +117,7 @@ class CandidateTableResolver implements ContextRewriter { return; } try { - Set<CandidateDim> candidates = new HashSet<CandidateDim>(); + Set<CandidateDim> candidates = new HashSet<>(); cubeql.getCandidateDimTables().put(dim, candidates); List<CubeDimensionTable> dimtables = cubeql.getMetastoreClient().getAllDimensionTables(dim); if (dimtables.isEmpty()) { @@ -126,7 +126,7 @@ class CandidateTableResolver implements ContextRewriter { "Dimension tables do not exist"); } else { log.info("Not considering optional dimension {} as, No dimension tables exist", dim); - removeOptionalDim(cubeql, dim); + removeOptionalDimWithoutAlias(cubeql, dim); } } for (CubeDimensionTable dimtable : dimtables) { @@ -139,44 +139,30 @@ class CandidateTableResolver implements ContextRewriter { } } - private void pruneOptionalDims(CubeQueryContext cubeql) { - Set<Dimension> tobeRemoved = new HashSet<Dimension>(); - Set<CandidateTable> allCandidates = new HashSet<CandidateTable>(); - allCandidates.addAll(cubeql.getCandidateFacts()); - for (Set<CandidateDim> cdims : cubeql.getCandidateDimTables().values()) { - allCandidates.addAll(cdims); - } - Set<CandidateTable> removedCandidates = new HashSet<CandidateTable>(); - for (Map.Entry<Dimension, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) { - Dimension dim = optdimEntry.getKey(); - OptionalDimCtx optdim = optdimEntry.getValue(); - Iterator<CandidateTable> iter = optdim.requiredForCandidates.iterator(); - while (iter.hasNext()) { - CandidateTable candidate = iter.next(); - if (!allCandidates.contains(candidate)) { - log.info("Removing candidate {} from requiredForCandidates of {}, as it is no more candidate", candidate, - dim); - iter.remove(); - removedCandidates.add(candidate); - } + private void removeOptionalDimWithoutAlias(CubeQueryContext cubeql, Dimension dim) { + for (Aliased<Dimension> aDim : cubeql.getOptionalDimensions()) { + if (aDim.getName().equals(dim.getName())) { + removeOptionalDim(cubeql, aDim); } } - Set<CandidateTable> candidatesReachableThroughRefs = new HashSet<CandidateTable>(); - for (Map.Entry<Dimension, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) { - Dimension dim = optdimEntry.getKey(); + } + + private void pruneOptionalDims(CubeQueryContext cubeql) { + Set<Aliased<Dimension>> tobeRemoved = new HashSet<>(); + for (Map.Entry<Aliased<Dimension>, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) { + Aliased<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 {} as all requiredForCandidates are removed", dim); tobeRemoved.add(dim); } } - for (Dimension dim : tobeRemoved) { + for (Aliased<Dimension> dim : tobeRemoved) { removeOptionalDim(cubeql, dim); } } - private void removeOptionalDim(CubeQueryContext cubeql, Dimension dim) { + private void removeOptionalDim(CubeQueryContext cubeql, Aliased<Dimension> dim) { OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().remove(dim); // remove all the depending candidate table as well for (CandidateTable candidate : optdim.requiredForCandidates) { @@ -240,7 +226,8 @@ class CandidateTableResolver implements ContextRewriter { // go over join chains and prune facts that dont have any of the columns in each chain for (JoinChain chain : cubeql.getJoinchains().values()) { - OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(cubeql.getCubeTbls().get(chain.getName())); + OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(Aliased.create((Dimension)cubeql.getCubeTbls() + .get(chain.getName()), chain.getName())); if (!checkForColumnExists(cfact, chain.getSourceColumns())) { // check if chain is optional or not if (optdim == null) { @@ -282,7 +269,7 @@ class CandidateTableResolver implements ContextRewriter { i.remove(); } } - Set<String> dimExprs = new HashSet<String>(cubeql.getQueriedExprs()); + Set<String> dimExprs = new HashSet<>(cubeql.getQueriedExprs()); dimExprs.removeAll(cubeql.getQueriedExprsWithMeasures()); if (cubeql.getCandidateFacts().size() == 0) { throw new LensException(LensCubeErrorCode.NO_FACT_HAS_COLUMN.getLensErrorInfo(), @@ -292,9 +279,9 @@ class CandidateTableResolver implements ContextRewriter { Set<Set<CandidateFact>> cfactset; if (queriedMsrs.isEmpty() && cubeql.getQueriedExprsWithMeasures().isEmpty()) { // if no measures are queried, add all facts individually as single covering sets - cfactset = new HashSet<Set<CandidateFact>>(); + cfactset = new HashSet<>(); for (CandidateFact cfact : cubeql.getCandidateFacts()) { - Set<CandidateFact> one = new LinkedHashSet<CandidateFact>(); + Set<CandidateFact> one = new LinkedHashSet<>(); one.add(cfact); cfactset.add(one); } @@ -302,7 +289,7 @@ class CandidateTableResolver implements ContextRewriter { } else { // Find out candidate fact table sets which contain all the measures // queried - List<CandidateFact> cfacts = new ArrayList<CandidateFact>(cubeql.getCandidateFacts()); + List<CandidateFact> cfacts = new ArrayList<>(cubeql.getCandidateFacts()); cfactset = findCoveringSets(cubeql, cfacts, queriedMsrs, cubeql.getQueriedExprsWithMeasures()); log.info("Measure covering fact sets :{}", cfactset); @@ -324,8 +311,8 @@ class CandidateTableResolver implements ContextRewriter { static Set<Set<CandidateFact>> findCoveringSets(CubeQueryContext cubeql, List<CandidateFact> cfactsPassed, Set<String> msrs, Set<String> exprsWithMeasures) { - Set<Set<CandidateFact>> cfactset = new HashSet<Set<CandidateFact>>(); - List<CandidateFact> cfacts = new ArrayList<CandidateFact>(cfactsPassed); + Set<Set<CandidateFact>> cfactset = new HashSet<>(); + List<CandidateFact> cfacts = new ArrayList<>(cfactsPassed); for (Iterator<CandidateFact> i = cfacts.iterator(); i.hasNext();) { CandidateFact cfact = i.next(); i.remove(); @@ -336,14 +323,14 @@ class CandidateTableResolver implements ContextRewriter { continue; } else if (cfact.getColumns().containsAll(msrs) && cubeql.getExprCtx().allEvaluable(cfact, exprsWithMeasures)) { // return single set - Set<CandidateFact> one = new LinkedHashSet<CandidateFact>(); + Set<CandidateFact> one = new LinkedHashSet<>(); one.add(cfact); cfactset.add(one); } else { // find the remaining measures in other facts if (i.hasNext()) { - Set<String> remainingMsrs = new HashSet<String>(msrs); - Set<String> remainingExprs = new HashSet<String>(exprsWithMeasures); + Set<String> remainingMsrs = new HashSet<>(msrs); + Set<String> remainingExprs = new HashSet<>(exprsWithMeasures); remainingMsrs.removeAll(cfact.getColumns()); remainingExprs.removeAll(cubeql.getExprCtx().coveringExpressions(exprsWithMeasures, cfact)); Set<Set<CandidateFact>> coveringSets = findCoveringSets(cubeql, cfacts, remainingMsrs, remainingExprs); @@ -366,9 +353,13 @@ class CandidateTableResolver implements ContextRewriter { if (cubeql.getAutoJoinCtx() == null) { return; } - Set<Dimension> allDims = new HashSet<Dimension>(cubeql.getDimensions()); + Set<Aliased<Dimension>> allDims = new HashSet<>(); + for (Dimension dim : cubeql.getDimensions()) { + allDims.add(Aliased.create(dim)); + } allDims.addAll(cubeql.getOptionalDimensions()); - for (Dimension dim : allDims) { + for (Aliased<Dimension> aliasedDim : allDims) { + Dimension dim = aliasedDim.getObject(); if (cubeql.getCandidateDimTables().get(dim) != null && !cubeql.getCandidateDimTables().get(dim).isEmpty()) { for (Iterator<CandidateDim> i = cubeql.getCandidateDimTables().get(dim).iterator(); i.hasNext();) { CandidateDim cdim = i.next(); @@ -377,11 +368,11 @@ class CandidateTableResolver implements ContextRewriter { // can participate in join // for each join path check for columns involved in path boolean removed = false; - for (Map.Entry<Dimension, Map<AbstractCubeTable, List<String>>> joincolumnsEntry : cubeql.getAutoJoinCtx() - .getJoinPathFromColumns().entrySet()) { - Dimension reachableDim = joincolumnsEntry.getKey(); + for (Map.Entry<Aliased<Dimension>, Map<AbstractCubeTable, List<String>>> joincolumnsEntry : cubeql + .getAutoJoinCtx().getJoinPathFromColumns().entrySet()) { + Aliased<Dimension> reachableDim = joincolumnsEntry.getKey(); OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(reachableDim); - Collection<String> colSet = joincolumnsEntry.getValue().get((AbstractCubeTable) dim); + Collection<String> colSet = joincolumnsEntry.getValue().get(dim); if (!checkForColumnExists(cdim, colSet)) { if (optdim == null || optdim.isRequiredInJoinChain @@ -397,11 +388,11 @@ class CandidateTableResolver implements ContextRewriter { } if (!removed) { // check for to columns - for (Map.Entry<Dimension, Map<AbstractCubeTable, List<String>>> joincolumnsEntry : cubeql.getAutoJoinCtx() - .getJoinPathToColumns().entrySet()) { - Dimension reachableDim = joincolumnsEntry.getKey(); + for (Map.Entry<Aliased<Dimension>, Map<AbstractCubeTable, List<String>>> joincolumnsEntry : cubeql + .getAutoJoinCtx().getJoinPathToColumns().entrySet()) { + Aliased<Dimension> reachableDim = joincolumnsEntry.getKey(); OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(reachableDim); - Collection<String> colSet = joincolumnsEntry.getValue().get((AbstractCubeTable) dim); + Collection<String> colSet = joincolumnsEntry.getValue().get(dim); if (!checkForColumnExists(cdim, colSet)) { if (optdim == null || optdim.isRequiredInJoinChain @@ -418,18 +409,18 @@ class CandidateTableResolver implements ContextRewriter { } if (!removed) { // go over the referenced columns accessed in the query and find out which tables can participate - if (cubeql.getOptionalDimensionMap().get(dim) != null - && !checkForColumnExists(cdim, cubeql.getOptionalDimensionMap().get(dim).colQueried)) { + if (cubeql.getOptionalDimensionMap().get(aliasedDim) != null + && !checkForColumnExists(cdim, cubeql.getOptionalDimensionMap().get(aliasedDim).colQueried)) { i.remove(); log.info("Not considering optional dimtable:{} as its denorm fields do not exist. Denorm fields:{}", - dimtable, cubeql.getOptionalDimensionMap().get(dim).colQueried); - cubeql.addDimPruningMsgs(dim, dimtable, - CandidateTablePruneCause.noColumnPartOfAJoinPath(cubeql.getOptionalDimensionMap().get(dim).colQueried)); + dimtable, cubeql.getOptionalDimensionMap().get(aliasedDim).colQueried); + cubeql.addDimPruningMsgs(dim, dimtable, CandidateTablePruneCause + .noColumnPartOfAJoinPath(cubeql.getOptionalDimensionMap().get(aliasedDim).colQueried)); } } } if (cubeql.getCandidateDimTables().get(dim).size() == 0) { - OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(dim); + OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(aliasedDim); if ((cubeql.getDimensions() != null && cubeql.getDimensions().contains(dim)) || (optdim != null && optdim.isRequiredInJoinChain)) { throw new LensException(LensCubeErrorCode.NO_DIM_HAS_COLUMN.getLensErrorInfo(), dim.getName(), @@ -438,7 +429,7 @@ class CandidateTableResolver implements ContextRewriter { // remove it from optional tables log.info("Not considering optional dimension {} as, No dimension table has the queried columns:{}" + " Clearing the required for candidates:{}", dim, optdim.colQueried, optdim.requiredForCandidates); - removeOptionalDim(cubeql, dim); + removeOptionalDim(cubeql, aliasedDim); } } } @@ -456,11 +447,12 @@ class CandidateTableResolver implements ContextRewriter { CubeFactTable fact = cfact.fact; // for each join path check for columns involved in path - for (Map.Entry<Dimension, Map<AbstractCubeTable, List<String>>> joincolumnsEntry : cubeql.getAutoJoinCtx() + for (Map.Entry<Aliased<Dimension>, Map<AbstractCubeTable, List<String>>> joincolumnsEntry : cubeql + .getAutoJoinCtx() .getJoinPathFromColumns().entrySet()) { - Dimension reachableDim = joincolumnsEntry.getKey(); + Aliased<Dimension> reachableDim = joincolumnsEntry.getKey(); OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(reachableDim); - colSet = joincolumnsEntry.getValue().get((AbstractCubeTable) cubeql.getCube()); + colSet = joincolumnsEntry.getValue().get(cubeql.getCube()); if (!checkForColumnExists(cfact, colSet)) { if (optdim == null || optdim.isRequiredInJoinChain @@ -486,21 +478,21 @@ class CandidateTableResolver implements ContextRewriter { * available in candidate tables that want to use references */ private void checkForSourceReachabilityForDenormCandidates(CubeQueryContext cubeql) { - if (cubeql.getOptionalDimensionMap().isEmpty()) { + if (cubeql.getOptionalDimensions().isEmpty()) { return; } if (cubeql.getAutoJoinCtx() == null) { - Set<Dimension> optionaldims = new HashSet<Dimension>(cubeql.getOptionalDimensions()); - for (Dimension dim : optionaldims) { + Set<Aliased<Dimension>> optionaldims = new HashSet<>(cubeql.getOptionalDimensions()); + for (Aliased<Dimension> dim : optionaldims) { log.info("Not considering optional dimension {} as, automatic join resolver is disbled ", dim); removeOptionalDim(cubeql, dim); } return; } // check for source columns for denorm columns - Map<Dimension, Set<CandidateTable>> removedCandidates = new HashMap<Dimension, Set<CandidateTable>>(); - for (Map.Entry<Dimension, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) { - Dimension dim = optdimEntry.getKey(); + Map<Aliased<Dimension>, Set<CandidateTable>> removedCandidates = new HashMap<>(); + for (Map.Entry<Aliased<Dimension>, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) { + Aliased<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 @@ -537,17 +529,17 @@ class CandidateTableResolver implements ContextRewriter { // 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>(); + for (Map.Entry<String, Set<Aliased<Dimension>>> dimColEntry : cubeql.getRefColToDim().entrySet()) { + Set<CandidateTable> candidatesReachableThroughRefs = new HashSet<>(); String col = dimColEntry.getKey(); - Set<Dimension> dimSet = dimColEntry.getValue(); - for (Dimension dim : dimSet) { + Set<Aliased<Dimension>> dimSet = dimColEntry.getValue(); + for (Aliased<Dimension> dim : dimSet) { OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(dim); if (optdim != null) { candidatesReachableThroughRefs.addAll(optdim.requiredForCandidates); } } - for (Dimension dim : dimSet) { + for (Aliased<Dimension> dim : dimSet) { if (removedCandidates.get(dim) != null) { for (CandidateTable candidate : removedCandidates.get(dim)) { if (!candidatesReachableThroughRefs.contains(candidate)) { @@ -582,11 +574,11 @@ class CandidateTableResolver implements ContextRewriter { // 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()) { + for (Map.Entry<QueriedExprColumn, Set<Aliased<Dimension>>> exprColEntry : cubeql.getExprColToDim().entrySet()) { QueriedExprColumn col = exprColEntry.getKey(); - Set<Dimension> dimSet = exprColEntry.getValue(); + Set<Aliased<Dimension>> dimSet = exprColEntry.getValue(); ExpressionContext ec = cubeql.getExprCtx().getExpressionContext(col.getExprCol(), col.getAlias()); - for (Dimension dim : dimSet) { + for (Aliased<Dimension> dim : dimSet) { if (removedCandidates.get(dim) != null) { for (CandidateTable candidate : removedCandidates.get(dim)) { // check if evaluable expressions of this candidate are no more evaluable because dimension is not reachable @@ -595,7 +587,7 @@ class CandidateTableResolver implements ContextRewriter { Iterator<ExprSpecContext> escIter = ec.getEvaluableExpressions().get(candidate).iterator(); while (escIter.hasNext()) { ExprSpecContext esc = escIter.next(); - if (esc.getExprDims().contains(dim)) { + if (esc.getExprDims().contains(dim.getObject())) { escIter.remove(); } } @@ -623,16 +615,16 @@ class CandidateTableResolver implements ContextRewriter { } // 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(); + Set<Aliased<Dimension>> tobeRemoved = new HashSet<>(); + for (Map.Entry<Aliased<Dimension>, OptionalDimCtx> optdimEntry : cubeql.getOptionalDimensionMap().entrySet()) { + Aliased<Dimension> dim = optdimEntry.getKey(); OptionalDimCtx optdim = optdimEntry.getValue(); if ((!optdim.colQueried.isEmpty() && optdim.requiredForCandidates.isEmpty()) && !optdim.isRequiredInJoinChain) { log.info("Not considering optional dimension {} as all requiredForCandidates are removed", dim); tobeRemoved.add(dim); } } - for (Dimension dim : tobeRemoved) { + for (Aliased<Dimension> dim : tobeRemoved) { removeOptionalDim(cubeql, dim); } } http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/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 06c2a0b..3e930de 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 @@ -96,13 +96,14 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { @Getter // Mapping of a qualified column name to its table alias - private final Map<String, String> colToTableAlias = new HashMap<String, String>(); + private final Map<String, String> colToTableAlias = new HashMap<>(); - @Getter() - private final Set<Set<CandidateFact>> candidateFactSets = new HashSet<Set<CandidateFact>>(); + @Getter + private final Set<Set<CandidateFact>> candidateFactSets = new HashSet<>(); + @Getter // would be added through join chains and de-normalized resolver - protected Map<Dimension, OptionalDimCtx> optionalDimensions = new HashMap<Dimension, OptionalDimCtx>(); + protected Map<Aliased<Dimension>, OptionalDimCtx> optionalDimensionMap = new HashMap<>(); // Alias to table object mapping of tables accessed in this query @Getter @@ -351,10 +352,10 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { // 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(); + private Map<String, Set<Aliased<Dimension>>> refColToDim = Maps.newHashMap(); - public void updateRefColDim(String col, Dimension dim) { - Set<Dimension> refDims = refColToDim.get(col.toLowerCase()); + public void updateRefColDim(String col, Aliased<Dimension> dim) { + Set<Aliased<Dimension>> refDims = refColToDim.get(col.toLowerCase()); if (refDims == null) { refDims = Sets.newHashSet(); refColToDim.put(col.toLowerCase(), refDims); @@ -371,12 +372,12 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { // 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(); + private Map<QueriedExprColumn, Set<Aliased<Dimension>>> exprColToDim = Maps.newHashMap(); - public void updateExprColDim(String tblAlias, String col, Dimension dim) { + public void updateExprColDim(String tblAlias, String col, Aliased<Dimension> dim) { QueriedExprColumn qexpr = new QueriedExprColumn(col, tblAlias); - Set<Dimension> exprDims = exprColToDim.get(qexpr); + Set<Aliased<Dimension>> exprDims = exprColToDim.get(qexpr); if (exprDims == null) { exprDims = Sets.newHashSet(); exprColToDim.put(qexpr, exprDims); @@ -419,10 +420,11 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { throw new LensException(LensCubeErrorCode.QUERIED_TABLE_NOT_FOUND.getLensErrorInfo(), alias); } Dimension dim = (Dimension) cubeTbls.get(alias); - OptionalDimCtx optDim = optionalDimensions.get(dim); + Aliased<Dimension> aliasedDim = Aliased.create(dim, alias); + OptionalDimCtx optDim = optionalDimensionMap.get(aliasedDim); if (optDim == null) { optDim = new OptionalDimCtx(); - optionalDimensions.put(dim, optDim); + optionalDimensionMap.put(aliasedDim, optDim); } if (cols != null && candidate != null) { for (String col : cols) { @@ -432,16 +434,16 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { } if (cubeCol != null) { if (isRef) { - updateRefColDim(cubeCol, dim); + updateRefColDim(cubeCol, aliasedDim); } else { - updateExprColDim(tableAlias, cubeCol, dim); + updateExprColDim(tableAlias, cubeCol, aliasedDim); } } if (!optDim.isRequiredInJoinChain) { optDim.isRequiredInJoinChain = isRequiredInJoin; } if (log.isDebugEnabled()) { - log.debug("Adding optional dimension:{} optDim:{} {} isRef:{}", dim, optDim, + log.debug("Adding optional dimension:{} optDim:{} {} isRef:{}", aliasedDim, optDim, (cubeCol == null ? "" : " for column:" + cubeCol), isRef); } } @@ -872,6 +874,7 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { public String toHQL() throws LensException { Set<CandidateFact> cfacts = pickCandidateFactToQuery(); Map<Dimension, CandidateDim> dimsToQuery = pickCandidateDimsToQuery(dimensions); + log.info("facts:{}, dimsToQuery: {}", cfacts, dimsToQuery); if (autoJoinCtx != null) { // prune join paths for picked fact and dimensions autoJoinCtx.pruneAllPaths(cube, cfacts, dimsToQuery); @@ -908,6 +911,7 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { exprDimensions.addAll(exprCtx.rewriteExprCtx(null, dimsToQuery, this)); } dimsToQuery.putAll(pickCandidateDimsToQuery(exprDimensions)); + log.info("facts:{}, dimsToQuery: {}", cfacts, dimsToQuery); // pick denorm tables for the picked fact and dimensions Set<Dimension> denormTables = new HashSet<Dimension>(); @@ -923,6 +927,7 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { denormTables.addAll(deNormCtx.rewriteDenormctx(null, dimsToQuery, false)); } dimsToQuery.putAll(pickCandidateDimsToQuery(denormTables)); + log.info("facts:{}, dimsToQuery: {}", cfacts, dimsToQuery); // Prune join paths once denorm tables are picked if (autoJoinCtx != null) { // prune join paths for picked fact and dimensions @@ -1129,12 +1134,8 @@ public class CubeQueryContext implements TrackQueriedColumns, QueryAST { } } - public Set<Dimension> getOptionalDimensions() { - return optionalDimensions.keySet(); - } - - public Map<Dimension, OptionalDimCtx> getOptionalDimensionMap() { - return optionalDimensions; + public Set<Aliased<Dimension>> getOptionalDimensions() { + return optionalDimensionMap.keySet(); } /** http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/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 26514d8..5ff265d 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 @@ -151,6 +151,7 @@ class ExpressionResolver implements ContextRewriter { } void addDirectlyAvailable(CandidateTable cTable) { + log.debug("Directly available in {}", cTable); directlyAvailableIn.add(cTable); } @@ -447,6 +448,7 @@ class ExpressionResolver implements ContextRewriter { } // Replace picked expressions in all the base trees replacePickedExpressions(queryAST); + log.debug("Picked expressions: {}", pickedExpressions); for (Set<PickedExpression> peSet : pickedExpressions.values()) { for (PickedExpression pe : peSet) { exprDims.addAll(pe.pickedCtx.exprDims); @@ -518,6 +520,7 @@ class ExpressionResolver implements ContextRewriter { for (ExpressionContext ec : ecSet) { if (ec.getSrcTable().getName().equals(cTable.getBaseTable().getName())) { if (!ec.directlyAvailableIn.contains(cTable)) { + log.debug("{} is not directly evaluable in {}", ec, cTable); if (ec.evaluableExpressions.get(cTable) != null && !ec.evaluableExpressions.get(cTable).isEmpty()) { // pick first evaluable expression Set<PickedExpression> peSet = pickedExpressions.get(ecEntry.getKey()); http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java index de5f95e..46b6bb7 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/StorageTableResolver.java @@ -156,7 +156,9 @@ class StorageTableResolver implements ContextRewriter { private void resolveDimStorageTablesAndPartitions(CubeQueryContext cubeql) throws LensException { Set<Dimension> allDims = new HashSet<Dimension>(cubeql.getDimensions()); - allDims.addAll(cubeql.getOptionalDimensions()); + for (Aliased<Dimension> dim : cubeql.getOptionalDimensions()) { + allDims.add(dim.getObject()); + } for (Dimension dim : allDims) { Set<CandidateDim> dimTables = cubeql.getCandidateDimTables().get(dim); if (dimTables == null || dimTables.isEmpty()) { http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/lens-cube/src/main/java/org/apache/lens/cube/parse/join/AutoJoinContext.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/join/AutoJoinContext.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/join/AutoJoinContext.java index 993955a..4c30d3f 100644 --- a/lens-cube/src/main/java/org/apache/lens/cube/parse/join/AutoJoinContext.java +++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/join/AutoJoinContext.java @@ -57,10 +57,10 @@ public class AutoJoinContext { // Map of a joined table to its columns which are part of any of the join // paths. This is used in candidate table resolver @Getter - private Map<Dimension, Map<AbstractCubeTable, List<String>>> joinPathFromColumns = new HashMap<>(); + private Map<Aliased<Dimension>, Map<AbstractCubeTable, List<String>>> joinPathFromColumns = new HashMap<>(); @Getter - private Map<Dimension, Map<AbstractCubeTable, List<String>>> joinPathToColumns = new HashMap<>(); + private Map<Aliased<Dimension>, Map<AbstractCubeTable, List<String>>> joinPathToColumns = new HashMap<>(); // there can be separate join clause for each fact in-case of multi fact queries @Getter @@ -122,12 +122,12 @@ public class AutoJoinContext { Map<AbstractCubeTable, List<String>> toColPaths = joinPathToColumns.get(joinPathEntry.getKey().getObject()); if (fromColPaths == null) { fromColPaths = new HashMap<>(); - joinPathFromColumns.put(joinPathEntry.getKey().getObject(), fromColPaths); + joinPathFromColumns.put(joinPathEntry.getKey(), fromColPaths); } if (toColPaths == null) { toColPaths = new HashMap<>(); - joinPathToColumns.put(joinPathEntry.getKey().getObject(), toColPaths); + joinPathToColumns.put(joinPathEntry.getKey(), toColPaths); } populateJoinPathCols(joinPaths, fromColPaths, toColPaths); } @@ -159,8 +159,8 @@ public class AutoJoinContext { } } - public void removeJoinedTable(Dimension dim) { - allPaths.remove(Aliased.create(dim)); + public void removeJoinedTable(Aliased<Dimension> dim) { + allPaths.remove(dim); joinPathFromColumns.remove(dim); } http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java ---------------------------------------------------------------------- diff --git a/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java b/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java index 4366938..caea3af 100644 --- a/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java +++ b/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java @@ -563,6 +563,10 @@ public class CubeTestSetup { chainRefs.add(new ChainRefCol("timedatechain2", "full_date")); cubeDimensions.add(new ReferencedDimAttribute(new FieldSchema("test_time_dim2", "date", "chained dim"), "Timedim full date", chainRefs, null, null, null, null)); + cubeDimensions.add(new BaseDimAttribute(new FieldSchema("cityid1", "int", "id to city"), + "City1", null, null, null)); + cubeDimensions.add(new BaseDimAttribute(new FieldSchema("cityid2", "int", "id to city"), + "City2", null, null, null)); Map<String, JoinChain> joinChains = new HashMap<>(); addCubeChains(joinChains, TEST_CUBE_NAME); @@ -717,6 +721,26 @@ public class CubeTestSetup { }); } }); + joinChains.put("cubeCity1", new JoinChain("cubeCity1", "cube-city", "city thru cube") { + { + addPath(new ArrayList<TableReference>() { + { + add(new TableReference(cubeName, "cityid1")); + add(new TableReference("citydim", "id")); + } + }); + } + }); + joinChains.put("cubeCity2", new JoinChain("cubeCity2", "cube-city", "city thru cube") { + { + addPath(new ArrayList<TableReference>() { + { + add(new TableReference(cubeName, "cityid2")); + add(new TableReference("citydim", "id")); + } + }); + } + }); joinChains.put("cubeState", new JoinChain("cubeState", "cube-state", "state thru cube") { { addPath(new ArrayList<TableReference>() { @@ -1345,6 +1369,7 @@ public class CubeTestSetup { // add dimensions of the cube factColumns.add(new FieldSchema("zipcode", "int", "zip")); factColumns.add(new FieldSchema("cityid", "int", "city id")); + factColumns.add(new FieldSchema("cityid1", "int", "city id")); factColumns.add(new FieldSchema("stateid", "int", "city id")); factColumns.add(new FieldSchema("test_time_dim_day_id", "int", "time id")); factColumns.add(new FieldSchema("test_time_dim_day_id2", "int", "time id")); @@ -1580,6 +1605,7 @@ public class CubeTestSetup { // add dimensions of the cube factColumns.add(new FieldSchema("zipcode", "int", "zip")); factColumns.add(new FieldSchema("cityid", "int", "city id")); + factColumns.add(new FieldSchema("cityid2", "int", "city id")); factColumns.add(new FieldSchema("test_time_dim_hour_id", "int", "time id")); factColumns.add(new FieldSchema("test_time_dim_hour_id2", "int", "time id")); factColumns.add(new FieldSchema("cdim2", "int", "cycledim id")); @@ -1714,6 +1740,8 @@ public class CubeTestSetup { // add dimensions of the cube factColumns.add(new FieldSchema("zipcode", "int", "zip")); factColumns.add(new FieldSchema("cityid", "int", "city id")); + factColumns.add(new FieldSchema("cityid1", "int", "city id")); + factColumns.add(new FieldSchema("cityid2", "int", "city id")); factColumns.add(new FieldSchema("stateid", "int", "state id")); factColumns.add(new FieldSchema("countryid", "int", "country id")); factColumns.add(new FieldSchema("dim1", "string", "dim1")); http://git-wip-us.apache.org/repos/asf/lens/blob/c7451f8e/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 a8390ef..d7707a9 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 @@ -267,7 +267,7 @@ public class TestDenormalizationResolver extends TestQueryRewrite { // candidate Assert.assertEquals(getLensExceptionErrorMessageInRewrite( "select citydim.name, citydim.statename, citydim.nocandidatecol " + "from citydim", conf), - "No dimension table has the queried columns " + "for citydim, columns: [name, statename, nocandidatecol]"); + "No dimension table has the queried columns " + "for citydim, columns: [name, statename, nocandidatecol]"); } @Test @@ -330,6 +330,20 @@ public class TestDenormalizationResolver extends TestQueryRewrite { } @Test + public void testTwoFieldsFromDifferentChainButSameTable() throws Exception { + String hqlQuery = rewrite("select cubecity1.name, cubecity2.name, msr2 from testcube where " + TWO_DAYS_RANGE, + conf); + String joinExpr = " join " + getDbName() + + "c1_citytable cubecity1 on testcube.cityid1 = cubecity1.id and (cubecity1.dt = 'latest') " + + " join " + getDbName() + + "c1_citytable cubecity2 on testcube.cityid2 = cubecity2.id and (cubecity2.dt = 'latest')"; + String expected = + getExpectedQuery("testcube", "select cubecity1.name, cubecity2.name, sum(testcube.msr2) FROM ", + joinExpr, null, " group by cubecity1.name, cubecity2.name", null, + getWhereForHourly2days("testcube", "c1_testfact2_raw")); + TestCubeRewriter.compareQueries(hqlQuery, expected); + } + @Test public void testDimensionQueryWithTwoRefCols() throws Exception { Configuration tConf = new Configuration(conf); tConf.set(CubeQueryConfUtil.DRIVER_SUPPORTED_STORAGES, "");