protected Statistics getMaterializedTableStats(Operator<?> sinkOp) {
    final Statistics tableStats = sinkOp.getStatistics().clone();
    if (tableStats.getColumnStatsState() == Statistics.State.NONE || sinkOp.getNumParent() == 0) {
      return tableStats;
    }

    final List<String> parentColumnNames = sinkOp.getParentOperators().get(0).getSchema().getColumnNames();
    final List<String> childColumnNames = sinkOp.getSchema().getColumnNames();
    if (parentColumnNames.size() != childColumnNames.size()) {
      LOG.warn("The number of columns of FileSinkOperator is inconsistent. Parent = {}, Child = {}",
          parentColumnNames, childColumnNames);
      tableStats.setColumnStatsState(Statistics.State.NONE);
      return tableStats;
    }
    final Map<String, String> mapping = new HashMap<>(parentColumnNames.size());
    for (int i = 0; i < parentColumnNames.size(); i++) {
      mapping.put(parentColumnNames.get(i), childColumnNames.get(i));
    }

    final List<ColStatistics> colStatsList = tableStats.getColumnStats();
    if (!mapping.keySet().equals(colStatsList.stream().map(ColStatistics::getColumnName).collect(Collectors.toSet()))) {
      LOG.warn("The column statistics are inconsistent with the expected column names. Actual = {}, Expected = {}",
          colStatsList, parentColumnNames);
      tableStats.setColumnStatsState(Statistics.State.NONE);
      return tableStats;
    }
    for (ColStatistics colStats : colStatsList) {
      colStats.setColumnName(mapping.get(colStats.getColumnName()));
    }
    tableStats.setColumnStats(colStatsList);
    return tableStats;
  }
Links tasks based on dependencies among CTEs which have actual tasks.
For example, when materialized CTE X depends on materialized CTE Y,
the leaf tasks of Y must have the root tasks of X as its child tasks.
ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzer.java