private void replaceReferencedColumns(CandidateFact cfact, boolean replaceFact)
     throws LensException {
   QueryAST ast = cubeql;
   if (replaceFact
       && (tableToRefCols.get(cfact.getName()) != null
           && !tableToRefCols.get(cfact.getName()).isEmpty())) {
     ast = cfact;
   }
   resolveClause(cubeql, ast.getSelectAST());
   resolveClause(cubeql, ast.getWhereAST());
   resolveClause(cubeql, ast.getGroupByAST());
   resolveClause(cubeql, ast.getHavingAST());
   resolveClause(cubeql, cubeql.getOrderByAST());
 }
    public Set<Dimension> rewriteDenormctx(
        CandidateFact cfact, Map<Dimension, CandidateDim> dimsToQuery, boolean replaceFact)
        throws LensException {
      Set<Dimension> refTbls = new HashSet<>();

      if (!tableToRefCols.isEmpty()) {
        // pick referenced columns for fact
        if (cfact != null) {
          pickColumnsForTable(cfact.getName());
        }
        // pick referenced columns for dimensions
        if (dimsToQuery != null && !dimsToQuery.isEmpty()) {
          for (CandidateDim cdim : dimsToQuery.values()) {
            pickColumnsForTable(cdim.getName());
          }
        }
        // Replace picked reference in all the base trees
        replaceReferencedColumns(cfact, replaceFact);

        // Add the picked references to dimsToQuery
        for (PickedReference picked : pickedRefs) {
          if (isPickedFor(picked, cfact, dimsToQuery)) {
            refTbls.add(
                (Dimension) cubeql.getCubeTableForAlias(picked.getChainRef().getChainName()));
            cubeql.addColumnsQueried(
                picked.getChainRef().getChainName(), picked.getChainRef().getRefColumn());
          }
        }
      }
      return refTbls;
    }
 private void addRangeClauses(CandidateFact fact) throws LensException {
   if (fact != null) {
     // resolve timerange positions and replace it by corresponding where clause
     for (TimeRange range : getTimeRanges()) {
       for (Map.Entry<String, String> entry :
           fact.getRangeToStorageWhereMap().get(range).entrySet()) {
         String table = entry.getKey();
         String rangeWhere = entry.getValue();
         if (!StringUtils.isBlank(rangeWhere)) {
           ASTNode rangeAST = HQLParser.parseExpr(rangeWhere);
           range.getParent().setChild(range.getChildIndex(), rangeAST);
         }
         fact.getStorgeWhereClauseMap().put(table, getWhereTree());
       }
     }
   }
 }
 private String getStorageStringWithAlias(
     CandidateFact fact, Map<Dimension, CandidateDim> dimsToQuery, String alias) {
   if (cubeTbls.get(alias) instanceof CubeInterface) {
     return fact.getStorageString(alias);
   } else {
     return dimsToQuery.get(cubeTbls.get(alias)).getStorageString(alias);
   }
 }
 // checks if the reference if picked for facts and dimsToQuery passed
 private boolean isPickedFor(
     PickedReference picked, CandidateFact cfact, Map<Dimension, CandidateDim> dimsToQuery) {
   if (cfact != null && picked.pickedFor.equalsIgnoreCase(cfact.getName())) {
     return true;
   }
   if (dimsToQuery != null) {
     for (CandidateDim cdim : dimsToQuery.values()) {
       if (picked.pickedFor.equalsIgnoreCase(cdim.getName())) {
         return true;
       }
     }
   }
   return false;
 }
 String getQBFromString(CandidateFact fact, Map<Dimension, CandidateDim> dimsToQuery)
     throws LensException {
   String fromString;
   if (getJoinAST() == null) {
     if (cube != null) {
       if (dimensions.size() > 0) {
         throw new LensException(LensCubeErrorCode.NO_JOIN_CONDITION_AVAILABLE.getLensErrorInfo());
       }
       fromString = fact.getStorageString(getAliasForTableName(cube.getName()));
     } else {
       if (dimensions.size() != 1) {
         throw new LensException(LensCubeErrorCode.NO_JOIN_CONDITION_AVAILABLE.getLensErrorInfo());
       }
       Dimension dim = dimensions.iterator().next();
       fromString = dimsToQuery.get(dim).getStorageString(getAliasForTableName(dim.getName()));
     }
   } else {
     StringBuilder builder = new StringBuilder();
     getQLString(qb.getQbJoinTree(), builder, fact, dimsToQuery);
     fromString = builder.toString();
   }
   return fromString;
 }
  /**
   * Find all de-normalized columns, if these columns are not directly available in candidate
   * tables, query will be replaced with the corresponding table reference
   */
  @Override
  public void rewriteContext(CubeQueryContext cubeql) throws LensException {
    DenormalizationContext denormCtx = cubeql.getDeNormCtx();
    if (denormCtx == null) {
      // Adds all the reference dimensions as eligible for denorm fields
      denormCtx = new DenormalizationContext(cubeql);
      cubeql.setDeNormCtx(denormCtx);
      // add ref columns in cube
      addRefColsQueried(cubeql, cubeql, denormCtx);
      // add ref columns from expressions
      for (Set<ExpressionContext> ecSet : cubeql.getExprCtx().getAllExprsQueried().values()) {
        for (ExpressionContext ec : ecSet) {
          for (ExprSpecContext esc : ec.getAllExprs()) {
            addRefColsQueried(cubeql, esc, denormCtx);
          }
        }
      }
    } else if (!denormCtx.tableToRefCols.isEmpty()) {
      // In the second iteration of denorm resolver
      // candidate tables which require denorm fields and the refernces are no
      // more valid will be pruned
      if (cubeql.getCube() != null && !cubeql.getCandidateFacts().isEmpty()) {
        for (Iterator<CandidateFact> i = cubeql.getCandidateFacts().iterator(); i.hasNext(); ) {
          CandidateFact cfact = i.next();
          if (denormCtx.tableToRefCols.containsKey(cfact.getName())) {
            for (ReferencedQueriedColumn refcol : denormCtx.tableToRefCols.get(cfact.getName())) {
              if (denormCtx.getReferencedCols().get(refcol.col.getName()).isEmpty()) {
                log.info(
                    "Not considering fact table:{} as column {} is not available",
                    cfact,
                    refcol.col);
                cubeql.addFactPruningMsgs(
                    cfact.fact, CandidateTablePruneCause.columnNotFound(refcol.col.getName()));
                i.remove();
              }
            }
          }
        }
        if (cubeql.getCandidateFacts().size() == 0) {
          throw new LensException(
              LensCubeErrorCode.NO_FACT_HAS_COLUMN.getLensErrorInfo(),
              cubeql.getColumnsQueried(cubeql.getCube().getName()).toString());
        }
        cubeql.pruneCandidateFactSet(CandidateTablePruneCode.COLUMN_NOT_FOUND);
      }
      if (cubeql.getDimensions() != null && !cubeql.getDimensions().isEmpty()) {
        for (Dimension dim : cubeql.getDimensions()) {
          for (Iterator<CandidateDim> i = cubeql.getCandidateDimTables().get(dim).iterator();
              i.hasNext(); ) {
            CandidateDim cdim = i.next();
            if (denormCtx.tableToRefCols.containsKey(cdim.getName())) {
              for (ReferencedQueriedColumn refcol : denormCtx.tableToRefCols.get(cdim.getName())) {
                if (denormCtx.getReferencedCols().get(refcol.col.getName()).isEmpty()) {
                  log.info(
                      "Not considering dim table:{} as column {} is not available",
                      cdim,
                      refcol.col);
                  cubeql.addDimPruningMsgs(
                      dim,
                      cdim.dimtable,
                      CandidateTablePruneCause.columnNotFound(refcol.col.getName()));
                  i.remove();
                }
              }
            }
          }

          if (cubeql.getCandidateDimTables().get(dim).size() == 0) {
            throw new LensException(
                LensCubeErrorCode.NO_DIM_HAS_COLUMN.getLensErrorInfo(),
                dim.toString(),
                cubeql.getColumnsQueried(dim.getName()).toString());
          }
        }
      }
    }
  }
  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);
    }

    Map<CandidateFact, Set<Dimension>> factDimMap = new HashMap<>();
    if (cfacts != null) {
      if (cfacts.size() > 1) {
        // copy ASTs for each fact
        for (CandidateFact cfact : cfacts) {
          cfact.copyASTs(this);
          factDimMap.put(cfact, new HashSet<>(dimsToQuery.keySet()));
        }
      }
      for (CandidateFact fact : cfacts) {
        addRangeClauses(fact);
      }
    }

    // pick dimension tables required during expression expansion for the picked fact and dimensions
    Set<Dimension> exprDimensions = new HashSet<Dimension>();
    if (cfacts != null) {
      for (CandidateFact cfact : cfacts) {
        Set<Dimension> factExprDimTables =
            exprCtx.rewriteExprCtx(cfact, dimsToQuery, cfacts.size() > 1 ? cfact : this);
        exprDimensions.addAll(factExprDimTables);
        if (cfacts.size() > 1) {
          factDimMap.get(cfact).addAll(factExprDimTables);
        }
      }
      if (cfacts.size() > 1) {
        havingAST = MultiFactHQLContext.pushDownHaving(havingAST, this, cfacts);
      }
    } else {
      // dim only query
      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>();
    if (cfacts != null) {
      for (CandidateFact cfact : cfacts) {
        Set<Dimension> factDenormTables =
            deNormCtx.rewriteDenormctx(cfact, dimsToQuery, cfacts.size() > 1);
        denormTables.addAll(factDenormTables);
        if (cfacts.size() > 1) {
          factDimMap.get(cfact).addAll(factDenormTables);
        }
      }
    } else {
      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
      autoJoinCtx.pruneAllPaths(cube, cfacts, dimsToQuery);
    }
    if (autoJoinCtx != null) {
      // add optional dims from Join resolver
      Set<Dimension> joiningTables = new HashSet<Dimension>();
      if (cfacts != null && cfacts.size() > 1) {
        for (CandidateFact cfact : cfacts) {
          Set<Dimension> factJoiningTables =
              autoJoinCtx.pickOptionalTables(cfact, factDimMap.get(cfact), this);
          factDimMap.get(cfact).addAll(factJoiningTables);
          joiningTables.addAll(factJoiningTables);
        }
      } else {
        joiningTables.addAll(autoJoinCtx.pickOptionalTables(null, dimsToQuery.keySet(), this));
      }
      dimsToQuery.putAll(pickCandidateDimsToQuery(joiningTables));
    }
    log.info("Picked Fact:{} dimsToQuery: {}", cfacts, dimsToQuery);
    pickedDimTables = dimsToQuery.values();
    pickedFacts = cfacts;
    if (cfacts != null) {
      if (cfacts.size() > 1) {
        // Update ASTs for each fact
        for (CandidateFact cfact : cfacts) {
          cfact.updateASTs(this);
        }
        whereAST =
            MultiFactHQLContext.convertHavingToWhere(
                havingAST, this, cfacts, new DefaultAliasDecider());
      }
    }
    hqlContext = createHQLContext(cfacts, dimsToQuery, factDimMap);
    return hqlContext.toHQL();
  }