@SuppressWarnings("nls")
  private Operator genTablePlan(String alias, QB qb) throws SemanticException {

    String alias_id = getAliasId(alias, qb);
    Table tab = qb.getMetaData().getSrcForAlias(alias);
    RowResolver rwsch;

    // is the table already present
    TableScanOperator top = topOps.get(alias_id);

    // Obtain table props in query
    Map<String, String> properties = qb.getTabPropsForAlias(alias);

    if (top == null) {
      // Determine row schema for TSOP.
      // Include column names from SerDe, the partition and virtual columns.
      rwsch = new RowResolver();
      try {
        // Including parameters passed in the query
        if (properties != null) {
          for (Entry<String, String> prop : properties.entrySet()) {
            if (tab.getSerdeParam(prop.getKey()) != null) {
              LOG.warn("SerDe property in input query overrides stored SerDe property");
            }
            tab.setSerdeParam(prop.getKey(), prop.getValue());
          }
        }
        // Obtain inspector for schema
        final Deserializer deserializer = tab.getDeserializer();
        StructObjectInspector rowObjectInspector = (StructObjectInspector) deserializer.getObjectInspector();

        deserializer.handleJobLevelConfiguration(conf);
        List<? extends StructField> fields = rowObjectInspector
            .getAllStructFieldRefs();
        Set<String> partCols = tab.hasNonNativePartitionSupport() ?
            Sets.newHashSet(tab.getPartColNames()) : Collections.emptySet();
        for (int i = 0; i < fields.size(); i++) {
          /**
           * if the column is a skewed column, use ColumnInfo accordingly
           */
          ColumnInfo colInfo = new ColumnInfo(fields.get(i).getFieldName(),
              TypeInfoUtils.getTypeInfoFromObjectInspector(fields.get(i)
                  .getFieldObjectInspector()), alias, false);
          if (partCols.contains(colInfo.getInternalName())) {
            colInfo.setHiddenPartitionCol(true);
          }
          colInfo.setSkewedCol(isSkewedCol(alias, qb, fields.get(i).getFieldName()));
          rwsch.put(alias, fields.get(i).getFieldName(), colInfo);
        }
      } catch (SerDeException e) {
        throw new RuntimeException(e);
      }
      // Hack!! - refactor once the metadata APIs with types are ready
      // Finally add the partitioning columns
      for (FieldSchema part_col : tab.getPartCols()) {
        LOG.trace("Adding partition col: " + part_col);
        rwsch.put(alias, part_col.getName(), new ColumnInfo(part_col.getName(),
            TypeInfoFactory.getPrimitiveTypeInfo(part_col.getType()), alias, true));
      }

      // put virtual columns into RowResolver.
      List<VirtualColumn> vcList = tab.getVirtualColumns();

      vcList.forEach(vc -> rwsch.put(alias, vc.getName().toLowerCase(), new ColumnInfo(vc.getName(),
              vc.getTypeInfo(), alias, true, vc.getIsHidden()
      )));

      // Create the root of the operator tree
      TableScanDesc tsDesc = new TableScanDesc(alias, vcList, tab);
      setupStats(tsDesc, qb.getParseInfo(), tab, alias, rwsch);

      Map<String, String> tblProperties = tab.getParameters();
      Map<String, String> tblPropertiesFromQuery = qb.getTabPropsForAlias(alias);

      AcidUtils.AcidOperationalProperties acidOperationalProperties = tsDesc.getAcidOperationalProperties();
      if (acidOperationalProperties != null) {
        tsDesc.getAcidOperationalProperties().setInsertOnlyFetchBucketId(
            (tblProperties != null && Boolean.parseBoolean(tblProperties.get(Constants.INSERT_ONLY_FETCH_BUCKET_ID))) ||
                (tblPropertiesFromQuery != null &&
                    Boolean.parseBoolean(tblPropertiesFromQuery.get(Constants.INSERT_ONLY_FETCH_BUCKET_ID))));

        tsDesc.getAcidOperationalProperties().setFetchDeletedRows(
            (tblProperties != null && Boolean.parseBoolean(tblProperties.get(Constants.ACID_FETCH_DELETED_ROWS))) ||
                (tblPropertiesFromQuery != null &&
                    Boolean.parseBoolean(tblPropertiesFromQuery.get(Constants.ACID_FETCH_DELETED_ROWS))));
      }

      SplitSample sample = nameToSplitSample.get(alias_id);
      if (sample != null && sample.getRowCount() != null) {
        tsDesc.setRowLimit(sample.getRowCount());
        nameToSplitSample.remove(alias_id);
      }

      top = (TableScanOperator) putOpInsertMap(OperatorFactory.get(getOpContext(), tsDesc,
          new RowSchema(rwsch.getColumnInfos())), rwsch);

      // Set insiderView so that we can skip the column authorization for this.
      top.setInsideView(qb.isInsideView() || qb.getAliasInsideView().contains(alias.toLowerCase()));

      // Add this to the list of top operators - we always start from a table
      // scan
      topOps.put(alias_id, top);

      if (properties != null) {
        tsDesc.setOpProps(properties);
      }
    } else {
      rwsch = opParseCtx.get(top).getRowResolver();
      top.setChildOperators(null);
    }

    // check if this table is sampled and needs more than input pruning
    Operator<? extends OperatorDesc> op = top;
    TableSample ts = qb.getParseInfo().getTabSample(alias);
    if (ts != null) {
      TableScanOperator tableScanOp = top;
      tableScanOp.getConf().setTableSample(ts);
      int num = ts.getNumerator();
      int den = ts.getDenominator();
      List<ASTNode> sampleExprs = ts.getExprs();

      // TODO: Do the type checking of the expressions
      List<String> tabBucketCols = tab.getBucketCols();
      int numBuckets = tab.getNumBuckets();

      // If there are no sample cols and no bucket cols then throw an error
      if (tabBucketCols.size() == 0 && sampleExprs.size() == 0) {
        throw new SemanticException(ErrorMsg.NON_BUCKETED_TABLE.getMsg() + " "
            + tab.getTableName());
      }

      if (num > den) {
        throw new SemanticException(
            ErrorMsg.BUCKETED_NUMERATOR_BIGGER_DENOMINATOR.getMsg() + " "
                + tab.getTableName());
      }

      // check if a predicate is needed
      // predicate is needed if either input pruning is not enough
      // or if input pruning is not possible

      // check if the sample columns are the same as the table bucket columns
      boolean colsEqual = true;
      if ((sampleExprs.size() != tabBucketCols.size())
          && (sampleExprs.size() != 0)) {
        colsEqual = false;
      }

      for (int i = 0; i < sampleExprs.size() && colsEqual; i++) {
        boolean colFound = false;
        for (int j = 0; j < tabBucketCols.size() && !colFound; j++) {
          if (sampleExprs.get(i).getToken().getType() != HiveParser.TOK_TABLE_OR_COL) {
            break;
          }

          if ((sampleExprs.get(i).getChild(0)).getText().equalsIgnoreCase(tabBucketCols.get(j))) {
            colFound = true;
          }
        }
        colsEqual = colFound;
      }

      // Check if input can be pruned
      ts.setInputPruning((sampleExprs.size() == 0 || colsEqual));

      // check if input pruning is enough
      if ((sampleExprs.size() == 0 || colsEqual)
          && (num == den || (den % numBuckets == 0 || numBuckets % den == 0))) {

        // input pruning is enough; add the filter for the optimizer to use it
        // later
        LOG.info("No need for sample filter");
        ExprNodeDesc samplePredicate = genSamplePredicate(ts, tabBucketCols,
            colsEqual, alias, rwsch, null,
                tab.getBucketingVersion());
        FilterDesc filterDesc = new FilterDesc(
            samplePredicate, true, new SampleDesc(ts.getNumerator(),
            ts.getDenominator(), tabBucketCols, true));
        filterDesc.setGenerated(true);
        op = OperatorFactory.getAndMakeChild(filterDesc,
            new RowSchema(rwsch.getColumnInfos()), top);
      } else {
        // need to add filter
        // create tableOp to be filterDesc and set as child to 'top'
        LOG.info("Need sample filter");
        ExprNodeDesc samplePredicate = genSamplePredicate(ts, tabBucketCols,
            colsEqual, alias, rwsch, null,
                tab.getBucketingVersion());
        FilterDesc filterDesc = new FilterDesc(samplePredicate, true);
        filterDesc.setGenerated(true);
        op = OperatorFactory.getAndMakeChild(filterDesc,
            new RowSchema(rwsch.getColumnInfos()), top);
      }
    } else {
      boolean testMode = conf.getBoolVar(ConfVars.HIVE_TEST_MODE);
      if (testMode) {
        String tabName = tab.getTableName();

        // has the user explicitly asked not to sample this table
        String unSampleTblList = conf
            .getVar(ConfVars.HIVE_TEST_MODE_NOSAMPLE);
        String[] unSampleTbls = unSampleTblList.split(",");
        boolean unsample = false;
        for (String unSampleTbl : unSampleTbls) {
          if (tabName.equalsIgnoreCase(unSampleTbl)) {
            unsample = true;
          }
        }

        if (!unsample) {
          int numBuckets = tab.getNumBuckets();

          // If the input table is bucketed, choose the first bucket
          if (numBuckets > 0) {
            TableSample tsSample = new TableSample(1, numBuckets);
            tsSample.setInputPruning(true);
            qb.getParseInfo().setTabSample(alias, tsSample);
            ExprNodeDesc samplePred = genSamplePredicate(tsSample, tab
                .getBucketCols(), true, alias, rwsch, null,
                    tab.getBucketingVersion());
            FilterDesc filterDesc = new FilterDesc(samplePred, true,
                new SampleDesc(tsSample.getNumerator(), tsSample
                    .getDenominator(), tab.getBucketCols(), true));
            filterDesc.setGenerated(true);
            op = OperatorFactory.getAndMakeChild(filterDesc,
                new RowSchema(rwsch.getColumnInfos()), top);
            LOG.info("No need for sample filter");
          } else {
            // The table is not bucketed, add a dummy filter :: rand()
            int freq = conf.getIntVar(ConfVars.HIVE_TEST_MODE_SAMPLE_FREQ);
            TableSample tsSample = new TableSample(1, freq);
            tsSample.setInputPruning(false);
            qb.getParseInfo().setTabSample(alias, tsSample);
            LOG.info("Need sample filter");
            ExprNodeDesc randFunc = ExprNodeTypeCheck.getExprNodeDefaultExprProcessor()
                .getFuncExprNodeDesc("rand",
                    new ExprNodeConstantDesc(Integer.valueOf(460476415)));
            ExprNodeDesc samplePred = genSamplePredicate(tsSample, null, false,
                alias, rwsch, randFunc, tab.getBucketingVersion());
            FilterDesc filterDesc = new FilterDesc(samplePred, true);
            filterDesc.setGenerated(true);
            op = OperatorFactory.getAndMakeChild(filterDesc,
                new RowSchema(rwsch.getColumnInfos()), top);
          }
        }
      }
    }

    Operator output = putOpInsertMap(op, rwsch);

    if (tab.isMaterializedTable()) {
      // Clone Statistics just in case because multiple TableScanOperator can access the same CTE
      top.setStatistics(ctx.getMaterializedTableStats(tab.getFullTableName()).clone());
    }

    LOG.debug("Created Table Plan for {} {}", alias, op);

    return output;
  }
Generates the sampling predicate from the TABLESAMPLE clause information.
This function uses the bucket column list to decide the expression inputs
to the predicate hash function in case useBucketCols is set to true,
otherwise the expression list stored in the TableSample is used. The bucket
columns of the table are used to generate this predicate in case no
expressions are provided on the TABLESAMPLE clause and the table has
clustering columns defined in it's metadata. The predicate created has the
following structure:

((hash(expressions) & Integer.MAX_VALUE) % denominator) == numerator

@param ts
         TABLESAMPLE clause information
@param bucketCols
         The clustering columns of the table
@param useBucketCols
         Flag to indicate whether the bucketCols should be used as input to
         the hash function
@param alias
         The alias used for the table in the row resolver
@param rwsch
         The row resolver used to resolve column references
@param planExpr
         The plan tree for the expression. If the user specified this, the
         parse expressions are not used
@return exprNodeDesc
@exception SemanticException
ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java