private Operator genSelOp(String command, boolean rewritten, Context origCtx)
      throws ParseException, SemanticException {
    // 1. initialization
    Context ctx = new Context(conf);
    origCtx.addSubContext(ctx);
    ctx.setOpContext(origCtx.getOpContext());
    ctx.setExplainConfig(origCtx.getExplainConfig());

    // 2. parse tree and create semantic analyzer. if we need to rewrite the analyze
    // statement, we do it now
    final ASTNode ast;
    final SemanticAnalyzer sem;
    final QueryState queryState = new QueryState.Builder().withHiveConf(conf).build();
    if (rewritten) {
      // Create the context object that is needed to store the column stats
      this.analyzeRewrite = ColumnStatsSemanticAnalyzer.genAnalyzeRewriteContext(conf, tbl);

      // The analyze statement has already been rewritten, we just need to create the AST
      // and the corresponding semantic analyzer
      ast = ParseUtils.parse(command, ctx);
      BaseSemanticAnalyzer baseSem = SemanticAnalyzerFactory.get(queryState, ast);
      sem = (SemanticAnalyzer) baseSem;
    } else {
      // We need to rewrite the analyze command and get the rewritten AST
      ASTNode analyzeTree = ParseUtils.parse(command, ctx);
      BaseSemanticAnalyzer baseSem = SemanticAnalyzerFactory.get(queryState, analyzeTree);
      ColumnStatsSemanticAnalyzer colSem = (ColumnStatsSemanticAnalyzer) baseSem;
      ast = colSem.rewriteAST(analyzeTree, this);

      // Obtain the context object that is needed to store the column stats
      this.analyzeRewrite = colSem.getAnalyzeRewriteContext();

      // Analyze the rewritten statement
      baseSem = SemanticAnalyzerFactory.get(queryState, ast);
      sem = (SemanticAnalyzer) baseSem;
    }
    QB qb = new QB(null, null, false);
    ASTNode child = ast;
    ParseContext subPCtx = sem.getParseContext();
    subPCtx.setContext(ctx);
    sem.initParseCtx(subPCtx);
    sem.doPhase1(child, qb, sem.initPhase1Ctx(), null);
    // This will trigger new calls to metastore to collect metadata
    // TODO: cache the information from the metastore
    sem.getMetaData(qb);
    sem.genPlan(qb);

    // 3. populate the load file work so that ColumnStatsTask can work
    loadFileWork.addAll(sem.getLoadFileWork());

    // 4. because there is only one TS for analyze statement, we can get it.
    if (sem.topOps.values().size() != 1) {
      throw new SemanticException(
          "ColumnStatsAutoGatherContext is expecting exactly one TS, but finds "
              + sem.topOps.values().size());
    }
    Operator<?> operator = sem.topOps.values().iterator().next();

    // 5. if this has been rewritten, get the SEL after UDTF;
    // otherwise, get the first SEL after TS
    if (rewritten) {
      while (!(operator instanceof UDTFOperator)) {
        operator = operator.getChildOperators().get(0);
      }
      operator = operator.getChildOperators().get(0);
    } else {
      while (!(operator instanceof SelectOperator)) {
        operator = operator.getChildOperators().get(0);
      }
    }
    return operator;
  }
Generate the statement of SELECT compute_stats(col1) compute_stats(col2),...,
similar to the one generated from ANALYZE TABLE t1 COMPUTE STATISTICS FOR COLUMNS,
but t1 is replaced by a TABLE(VALUES(cast(null as int),cast(null as string))) AS t1(col1,col2).

We use TABLE-VALUES statement for computing stats for CTAS statement because in those cases
the table has not been created yet. Once the plan for the SELECT statement is generated,
we connect it to the existing CTAS plan as we do for INSERT or INSERT OVERWRITE.
ql/src/java/org/apache/hadoop/hive/ql/parse/ColumnStatsAutoGatherContext.java