Java tutorial
/** * 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.lens.cube.parse; import java.util.*; import org.apache.lens.cube.error.LensCubeErrorCode; 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.ExpressionContext; import org.apache.lens.server.api.error.LensException; import org.apache.commons.lang.StringUtils; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; /** * This resolver prunes the candidate tables for following cases * <p/> * 1. If queried dim attributes are not present. Also Figures out if queried column is not part of candidate table, but * is a denormalized field which can reached through a reference 2. Finds all the candidate fact sets containing queried * measures. Prunes facts which do not contain any of the queried measures. 3. Required join columns are not part of * candidate tables 4. Required source columns(join columns) for reaching a denormalized field, are not part of * candidate tables 5. Required denormalized fields are not part of refered tables, there by all the candidates which * are using denormalized fields. */ @Slf4j class CandidateTableResolver implements ContextRewriter { private boolean checkForQueriedColumns = true; @Override public void rewriteContext(CubeQueryContext cubeql) throws LensException { if (checkForQueriedColumns) { log.debug("Dump queried columns:{}", cubeql.getTblAliasToColumns()); populateCandidateTables(cubeql); resolveCandidateFactTables(cubeql); resolveCandidateDimTables(cubeql); // remove optional dims added whom requiredForCandidates have been removed pruneOptionalDims(cubeql); checkForQueriedColumns = false; } else { // populate optional tables for (Aliased<Dimension> dim : cubeql.getOptionalDimensions()) { log.info("Populating optional dim:{}", dim); populateDimTables(dim.getObject(), cubeql, true); } if (cubeql.getAutoJoinCtx() != null) { // Before checking for candidate table columns, prune join paths containing non existing columns // in populated candidate tables cubeql.getAutoJoinCtx().pruneAllPaths(cubeql.getCube(), CandidateUtil.getColumnsFromCandidates(cubeql.getCandidates()), null); cubeql.getAutoJoinCtx().pruneAllPathsForCandidateDims(cubeql.getCandidateDimTables()); cubeql.getAutoJoinCtx().refreshJoinPathColumns(); } checkForSourceReachabilityForDenormCandidates(cubeql); // check for joined columns and denorm columns on refered tables resolveCandidateFactTablesForJoins(cubeql); resolveCandidateDimTablesForJoinsAndDenorms(cubeql); checkForQueriedColumns = true; } } private void populateCandidateTables(CubeQueryContext cubeql) throws LensException { if (cubeql.getCube() != null) { List<FactTable> factTables = cubeql.getMetastoreClient().getAllFacts(cubeql.getCube()); if (factTables.isEmpty()) { throw new LensException(LensCubeErrorCode.NO_CANDIDATE_FACT_AVAILABLE.getLensErrorInfo(), cubeql.getCube().getName() + " does not have any facts"); } for (FactTable fact : factTables) { if (fact.getUpdatePeriods().isEmpty()) { log.info("Not considering fact: {} as it has no update periods", fact.getName()); } else { for (String s : fact.getStorages()) { StorageCandidate sc = new StorageCandidate(cubeql.getCube(), fact, s, cubeql); cubeql.getCandidates().add(sc); } } } log.info("Populated storage candidates: {}", cubeql.getCandidates()); List<SegmentationCandidate> segmentationCandidates = Lists.newArrayList(); for (Segmentation segmentation : cubeql.getMetastoreClient().getAllSegmentations(cubeql.getCube())) { segmentationCandidates.add(new SegmentationCandidate(cubeql, segmentation)); } cubeql.getCandidates().addAll(segmentationCandidates); } if (cubeql.getDimensions().size() != 0) { for (Dimension dim : cubeql.getDimensions()) { populateDimTables(dim, cubeql, false); } } } private void populateDimTables(Dimension dim, CubeQueryContext cubeql, boolean optional) throws LensException { if (cubeql.getCandidateDimTables().get(dim) != null) { return; } Set<CandidateDim> candidates = new HashSet<>(); cubeql.getCandidateDimTables().put(dim, candidates); List<CubeDimensionTable> dimtables = cubeql.getMetastoreClient().getAllDimensionTables(dim); if (dimtables.isEmpty()) { if (!optional) { throw new LensException(LensCubeErrorCode.NO_CANDIDATE_DIM_AVAILABLE.getLensErrorInfo(), dim.getName().concat(" has no dimension tables")); } else { log.info("Not considering optional dimension {} as, No dimension tables exist", dim); removeOptionalDimWithoutAlias(cubeql, dim); } } for (CubeDimensionTable dimtable : dimtables) { CandidateDim cdim = new CandidateDim(dimtable, dim); candidates.add(cdim); } log.info("Populated candidate dims: {} for {}", cubeql.getCandidateDimTables().get(dim), dim); } private void removeOptionalDimWithoutAlias(CubeQueryContext cubeql, Dimension dim) { for (Aliased<Dimension> aDim : cubeql.getOptionalDimensions()) { if (aDim.getName().equals(dim.getName())) { removeOptionalDim(cubeql, aDim); } } } 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(); 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 (Aliased<Dimension> dim : tobeRemoved) { removeOptionalDim(cubeql, 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) { if (candidate instanceof StorageCandidate) { log.info("Not considering storage candidate:{} as refered table does not have any valid dimtables", candidate); cubeql.getCandidates().remove(candidate); cubeql.addStoragePruningMsg(((StorageCandidate) candidate), new CandidateTablePruneCause(CandidateTablePruneCode.INVALID_DENORM_TABLE)); } else { log.info("Not considering dimtable:{} as refered table does not have any valid dimtables", candidate); cubeql.getCandidateDimTables().get(((CandidateDim) candidate).getBaseTable()).remove(candidate); cubeql.addDimPruningMsgs((Dimension) candidate.getBaseTable(), (CubeDimensionTable) candidate.getTable(), new CandidateTablePruneCause(CandidateTablePruneCode.INVALID_DENORM_TABLE)); } } // remove join paths corresponding to the dim if (cubeql.getAutoJoinCtx() != null) { cubeql.getAutoJoinCtx().removeJoinedTable(dim); } } private static boolean isColumnAvailableInRange(final TimeRange range, Date startTime, Date endTime) { return (isColumnAvailableFrom(range.getFromDate(), startTime) && isColumnAvailableTill(range.getToDate(), endTime)); } private static boolean isColumnAvailableFrom(@NonNull final Date date, Date startTime) { return (startTime == null) || (date.equals(startTime) || date.after(startTime)); } private static boolean isColumnAvailableTill(@NonNull final Date date, Date endTime) { return (endTime == null) || (date.equals(endTime) || date.before(endTime)); } private static boolean isFactColumnValidForRange(CubeQueryContext cubeql, CandidateTable cfact, String col) { for (TimeRange range : cubeql.getTimeRanges()) { if (!isColumnAvailableInRange(range, getFactColumnStartTime(cfact, col), getFactColumnEndTime(cfact, col))) { return false; } } return true; } private static Date getFactColumnStartTime(CandidateTable table, String factCol) { Date startTime = null; if (table instanceof StorageCandidate) { for (String key : ((StorageCandidate) table).getFact().getProperties().keySet()) { if (key.contains(MetastoreConstants.FACT_COL_START_TIME_PFX)) { String propCol = StringUtils.substringAfter(key, MetastoreConstants.FACT_COL_START_TIME_PFX); if (factCol.equals(propCol)) { startTime = MetastoreUtil.getDateFromProperty( ((StorageCandidate) table).getFact().getProperties().get(key), false, true); } } } } return startTime; } private static Date getFactColumnEndTime(CandidateTable table, String factCol) { Date endTime = null; if (table instanceof StorageCandidate) { for (String key : ((StorageCandidate) table).getFact().getProperties().keySet()) { if (key.contains(MetastoreConstants.FACT_COL_END_TIME_PFX)) { String propCol = StringUtils.substringAfter(key, MetastoreConstants.FACT_COL_END_TIME_PFX); if (factCol.equals(propCol)) { endTime = MetastoreUtil.getDateFromProperty( ((StorageCandidate) table).getFact().getProperties().get(key), false, true); } } } } return endTime; } private void resolveCandidateFactTables(CubeQueryContext cubeql) throws LensException { if (cubeql.getCube() != null) { String str = cubeql.getConf().get(CubeQueryConfUtil.getValidFactTablesKey(cubeql.getCube().getName())); List<String> validFactTables = StringUtils.isBlank(str) ? null : Arrays.asList(StringUtils.split(str.toLowerCase(), ",")); Set<QueriedPhraseContext> queriedMsrs = new HashSet<>(); Set<QueriedPhraseContext> dimExprs = new HashSet<>(); for (QueriedPhraseContext qur : cubeql.getQueriedPhrases()) { if (qur.hasMeasures(cubeql)) { queriedMsrs.add(qur); } else { dimExprs.add(qur); } } // Remove storage candidates based on whether they are valid or not. for (Iterator<Candidate> i = cubeql.getCandidates().iterator(); i.hasNext();) { Candidate cand = i.next(); if (cand instanceof StorageCandidate) { StorageCandidate sc = (StorageCandidate) cand; if (validFactTables != null) { if (!validFactTables.contains(sc.getFact().getName().toLowerCase())) { log.info("Not considering storage candidate:{} as it is not a valid candidate", sc); cubeql.addStoragePruningMsg(sc, new CandidateTablePruneCause(CandidateTablePruneCode.INVALID)); i.remove(); continue; } } // update expression evaluability for this fact for (String expr : cubeql.getQueriedExprs()) { cubeql.getExprCtx().updateEvaluables(expr, sc); } } // go over the columns accessed in the query and find out which tables // can answer the query // the candidate facts should have all the dimensions queried and // atleast // one measure boolean toRemove = false; for (QueriedPhraseContext qur : dimExprs) { if (!cand.isPhraseAnswerable(qur)) { log.info("Not considering storage candidate:{} as columns {} are not available", cand, qur.getColumns()); cubeql.addStoragePruningMsg(cand, CandidateTablePruneCause.columnNotFound(qur.getColumns())); toRemove = true; break; } } // check if the candidate fact has atleast one measure queried // if expression has measures, they should be considered along with other measures and see if the fact can be // part of measure covering set if (!checkForFactColumnExistsAndValidForRange(cand, queriedMsrs)) { Set<String> columns = getColumns(queriedMsrs); log.info("Not considering storage candidate:{} as columns {} is not available", cand, columns); cubeql.addStoragePruningMsg(cand, CandidateTablePruneCause.columnNotFound(columns)); toRemove = true; } // go over join chains and prune facts that dont have any of the columns in each chain if (cand instanceof StorageCandidate) { StorageCandidate sc = (StorageCandidate) cand; for (JoinChain chain : cubeql.getJoinchains().values()) { OptionalDimCtx optdim = cubeql.getOptionalDimensionMap().get(Aliased .create((Dimension) cubeql.getCubeTbls().get(chain.getName()), chain.getName())); if (!checkForFactColumnExistsAndValidForRange(sc, chain.getSourceColumns(), cubeql)) { // check if chain is optional or not if (optdim == null) { log.info("Not considering storage candidate:{} as columns {} are not available", sc, chain.getSourceColumns()); cubeql.addStoragePruningMsg(sc, CandidateTablePruneCause.columnNotFound(chain.getSourceColumns())); toRemove = true; break; } } } } if (toRemove) { i.remove(); } } if (cubeql.getCandidates().size() == 0) { throw new LensException(LensCubeErrorCode.NO_FACT_HAS_COLUMN.getLensErrorInfo(), getColumns(cubeql.getQueriedPhrases()).toString()); } } } private static Set<String> getColumns(Collection<QueriedPhraseContext> queriedPhraseContexts) { Set<String> cols = new HashSet<>(); for (QueriedPhraseContext qur : queriedPhraseContexts) { cols.addAll(qur.getColumns()); } return cols; } private void resolveCandidateDimTablesForJoinsAndDenorms(CubeQueryContext cubeql) throws LensException { if (cubeql.getAutoJoinCtx() == null) { return; } Set<Aliased<Dimension>> allDims = new HashSet<>(); for (Dimension dim : cubeql.getDimensions()) { allDims.add(Aliased.create(dim)); } allDims.addAll(cubeql.getOptionalDimensions()); 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(); CubeDimensionTable dimtable = cdim.dimtable; // go over the join columns accessed in the query and find out which tables // can participate in join // for each join path check for columns involved in path boolean removed = false; 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(dim); if (!checkForFactColumnExistsAndValidForRange(cdim, colSet, cubeql)) { if (optdim == null || optdim.isRequiredInJoinChain || optdim.requiredForCandidates.contains(cdim)) { i.remove(); removed = true; log.info( "Not considering dimtable:{} as its columns are not part of any join paths. Join columns:{}", dimtable, colSet); cubeql.addDimPruningMsgs(dim, dimtable, CandidateTablePruneCause.noColumnPartOfAJoinPath(colSet)); break; } } } if (!removed) { // check for to columns 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(dim); if (!checkForFactColumnExistsAndValidForRange(cdim, colSet, cubeql)) { if (optdim == null || optdim.isRequiredInJoinChain || optdim.requiredForCandidates.contains(cdim)) { i.remove(); removed = true; log.info( "Not considering dimtable:{} as its columns are not part of any join paths. Join columns:{}", dimtable, colSet); cubeql.addDimPruningMsgs(dim, dimtable, CandidateTablePruneCause.noColumnPartOfAJoinPath(colSet)); break; } } } } if (!removed) { // go over the referenced columns accessed in the query and find out which tables can participate if (cubeql.getOptionalDimensionMap().get(aliasedDim) != null && !checkForFactColumnExistsAndValidForRange(cdim, cubeql.getOptionalDimensionMap().get(aliasedDim).colQueried, cubeql)) { i.remove(); log.info( "Not considering optional dimtable:{} as its denorm fields do not exist. Denorm fields:{}", 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(aliasedDim); if ((cubeql.getDimensions() != null && cubeql.getDimensions().contains(dim)) || (optdim != null && optdim.isRequiredInJoinChain)) { throw new LensException(LensCubeErrorCode.NO_DIM_HAS_COLUMN.getLensErrorInfo(), dim.getName(), cubeql.getAutoJoinCtx().getAllJoinPathColumnsOfTable(dim).toString()); } else { // 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, aliasedDim); } } } } } private void resolveCandidateFactTablesForJoins(CubeQueryContext cubeql) throws LensException { if (cubeql.getAutoJoinCtx() == null) { return; } Collection<String> colSet = null; if (cubeql.getCube() != null && !cubeql.getCandidates().isEmpty()) { for (Iterator<StorageCandidate> i = CandidateUtil.getStorageCandidates(cubeql.getCandidates()) .iterator(); i.hasNext();) { StorageCandidate sc = i.next(); // for each join path check for columns involved in path 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); colSet = joincolumnsEntry.getValue().get(cubeql.getCube()); if (!checkForFactColumnExistsAndValidForRange(sc, colSet, cubeql)) { if (optdim == null || optdim.isRequiredInJoinChain || optdim.requiredForCandidates.contains(sc)) { i.remove(); log.info( "Not considering storage candidate :{} as it does not have columns in any of the join paths." + " Join columns:{}", sc, colSet); cubeql.addStoragePruningMsg(sc, CandidateTablePruneCause.noColumnPartOfAJoinPath(colSet)); break; } } } } if (cubeql.getCandidates().size() == 0) { throw new LensException(LensCubeErrorCode.NO_FACT_HAS_COLUMN.getLensErrorInfo(), colSet == null ? "NULL" : colSet.toString()); } } } /** * This method checks if the source columns(resolved through automatic join resolver) for reaching the references are * available in candidate tables that want to use references */ private void checkForSourceReachabilityForDenormCandidates(CubeQueryContext cubeql) { if (cubeql.getOptionalDimensions().isEmpty()) { return; } if (cubeql.getAutoJoinCtx() == null) { 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<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 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 {} from requiredForCandidates of {}, as no join paths exist", candidate, dim); remove = true; } else { List<String> colSet = cubeql.getAutoJoinCtx().getJoinPathFromColumns().get(dim) .get(candidate.getBaseTable()); if (!checkForFactColumnExistsAndValidForRange(candidate, colSet, cubeql)) { log.info( "Removing candidate {} from requiredForCandidates of {}, as columns:{} do not exist", candidate, dim, colSet); remove = true; } } if (remove) { iter.remove(); cTableSet.add(candidate); } } } // 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<Aliased<Dimension>>> dimColEntry : cubeql.getRefColToDim().entrySet()) { Set<CandidateTable> candidatesReachableThroughRefs = new HashSet<>(); String col = dimColEntry.getKey(); 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 (Aliased<Dimension> dim : dimSet) { if (removedCandidates.get(dim) != null) { for (CandidateTable candidate : removedCandidates.get(dim)) { if (!candidatesReachableThroughRefs.contains(candidate)) { if (candidate instanceof StorageCandidate) { if (cubeql.getCandidates().contains(candidate)) { log.info( "Not considering Storage:{} as its required optional dims are not reachable", candidate); cubeql.getCandidates().remove(candidate); cubeql.addStoragePruningMsg((StorageCandidate) candidate, CandidateTablePruneCause.columnNotFound(col)); Collection<Candidate> prunedCandidates = CandidateUtil .filterCandidates(cubeql.getCandidates(), (StorageCandidate) candidate); cubeql.addCandidatePruningMsg(prunedCandidates, new CandidateTablePruneCause( CandidateTablePruneCode.ELEMENT_IN_SET_PRUNED)); } } else if (cubeql.getCandidateDimTables() .containsKey(((CandidateDim) candidate).getBaseTable())) { log.info( "Not considering dimtable:{} as its required optional dims are not reachable", candidate); 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<Aliased<Dimension>>> exprColEntry : cubeql.getExprColToDim() .entrySet()) { QueriedExprColumn col = exprColEntry.getKey(); Set<Aliased<Dimension>> dimSet = exprColEntry.getValue(); ExpressionContext ec = cubeql.getExprCtx().getExpressionContext(col.getExprCol(), col.getAlias()); 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 // if no evaluable expressions exist, then remove the candidate if (ec.getEvaluableExpressions().get(candidate) != null) { ec.getEvaluableExpressions().get(candidate) .removeIf(esc -> esc.getExprDims().contains(dim.getObject())); } if (cubeql.getExprCtx().isEvaluable(col.getExprCol(), candidate)) { // candidate has other evaluable expressions continue; } if (candidate instanceof StorageCandidate) { if (cubeql.getCandidates().contains(candidate)) { log.info("Not considering fact:{} as is not reachable through any optional dim", candidate); cubeql.getCandidates().remove(candidate); cubeql.addStoragePruningMsg(((StorageCandidate) candidate), CandidateTablePruneCause.expressionNotEvaluable(col.getExprCol())); } } else if (cubeql.getCandidateDimTables() .containsKey(((CandidateDim) candidate).getBaseTable())) { log.info("Not considering dimtable:{} as is not reachable through any optional dim", candidate); 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<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 (Aliased<Dimension> dim : tobeRemoved) { removeOptionalDim(cubeql, dim); } } private void resolveCandidateDimTables(CubeQueryContext cubeql) throws LensException { if (cubeql.getDimensions().size() != 0) { for (Dimension dim : cubeql.getDimensions()) { // go over the columns accessed in the query and find out which tables // can answer the query for (Iterator<CandidateDim> i = cubeql.getCandidateDimTables().get(dim).iterator(); i.hasNext();) { CandidateDim cdim = i.next(); if (cubeql.getColumnsQueriedForTable(dim.getName()) != null) { for (String col : cubeql.getColumnsQueriedForTable(dim.getName())) { if (!cdim.getColumns().contains(col.toLowerCase())) { // check if the column is an expression if (cdim.getBaseTable().getExpressionNames().contains(col)) { cubeql.getExprCtx().updateEvaluables(col, cdim); // check if the expression is evaluable if (!cubeql.getExprCtx().isEvaluable(col, cdim)) { log.info("Not considering dimtable: {} as expression {} is not evaluable", cdim, col); cubeql.addDimPruningMsgs(dim, cdim.getTable(), CandidateTablePruneCause.expressionNotEvaluable(col)); i.remove(); break; } } else if (!cubeql.getDeNormCtx().addRefUsage(cubeql, cdim, col, dim.getName())) { // check if it available as reference, if not remove the // candidate log.info("Not considering dimtable: {} as column {} is not available", cdim, col); cubeql.addDimPruningMsgs(dim, cdim.getTable(), CandidateTablePruneCause.columnNotFound(col)); i.remove(); break; } } } } } if (cubeql.getCandidateDimTables().get(dim).size() == 0) { throw new LensException(LensCubeErrorCode.NO_DIM_HAS_COLUMN.getLensErrorInfo(), dim.getName(), cubeql.getColumnsQueriedForTable(dim.getName()).toString()); } } } } // The candidate table contains atleast one column in the colSet and // column can the queried in the range specified private static boolean checkForFactColumnExistsAndValidForRange(CandidateTable table, Collection<String> colSet, CubeQueryContext cubeql) { if (colSet == null || colSet.isEmpty()) { return true; } for (String column : colSet) { if (table.getColumns().contains(column) && isFactColumnValidForRange(cubeql, table, column)) { return true; } } return false; } private static boolean checkForFactColumnExistsAndValidForRange(Candidate sc, Collection<QueriedPhraseContext> colSet) throws LensException { if (colSet == null || colSet.isEmpty()) { return true; } boolean isEvaluable = false; for (QueriedPhraseContext qur : colSet) { if (sc.isPhraseAnswerable(qur)) { isEvaluable = true; continue; } } return isEvaluable; } }