private List<AnalyzeWork> processOneTable(TableName fullTableName, Map<String, Boolean> dbsToSkip)
      throws MetaException, NoSuchTxnException, NoSuchObjectException {
    if (isAnalyzeTableInProgress(fullTableName)) return null;
    String cat = fullTableName.getCat(), db = fullTableName.getDb(), tbl = fullTableName.getTable();
    String dbName = MetaStoreUtils.prependCatalogToDbName(cat,db, conf);
    if (!dbsToSkip.containsKey(dbName)) {
      dbsToSkip.put(dbName, MetaStoreUtils.checkIfDbNeedsToBeSkipped(rs.getDatabase(cat, db)));
    }
    if (dbsToSkip.get(dbName)) {
      LOG.debug("Skipping table {}", tbl);
      return null;
    }
    Table table = rs.getTable(cat, db, tbl);
    LOG.debug("Processing table {}", table);

    // Check if the table should be skipped.
    String skipParam = table.getParameters().get(SKIP_STATS_AUTOUPDATE_PROPERTY);
    if ("true".equalsIgnoreCase(skipParam)) return null;

    // Note: ideally we should take a lock here to pretend to be a real reader.
    //       For now, this check is going to have race potential; it may run a spurious analyze.
    String writeIdString = null;
    boolean isTxn = AcidUtils.isTransactionalTable(table);
    if (isTxn) {
      if (!areTxnStatsEnabled) return null; // Skip transactional tables.
      ValidReaderWriteIdList writeIds = getWriteIds(fullTableName);
      if (writeIds == null) {
        LOG.error("Cannot get writeIds for transactional table " + fullTableName + "; skipping");
        return null;
      }
      writeIdString = writeIds.writeToString();
    }
    List<String> allCols = new ArrayList<>(table.getSd().getColsSize());
    for (FieldSchema fs : table.getSd().getCols()) {
      allCols.add(fs.getName());
    }
    Collections.sort(allCols);
    if (table.getPartitionKeysSize() == 0) {
      Map<String, String> params = table.getParameters();
      List<String> colsToUpdate = null;
      long writeId = isTxn ? table.getWriteId() : -1;
      if (isExistingOnly) {
        // Get the existing stats, including the txn state if any, to see if we need to update.
        colsToUpdate = getExistingNonPartTableStatsToUpdate(
            fullTableName, cat, db, tbl, params, writeId, allCols, writeIdString);
      } else {
        colsToUpdate = getAnyStatsToUpdate(db, tbl, allCols, params, writeId, writeIdString);
      }

      LOG.debug("Columns to update are {}; existing only: {}, out of: {} based on {}",
          colsToUpdate, isExistingOnly, allCols, params);

      if (colsToUpdate == null || colsToUpdate.isEmpty()) {
        return null; // No update necessary.
      }
      return Lists.newArrayList(new AnalyzeWork(fullTableName,
          null, null, allCols.size() == colsToUpdate.size() ? null : colsToUpdate));
    } else {
      Map<String, List<String>> partsToAnalyze = new HashMap<>();
      List<String> colsForAllParts = findPartitionsToAnalyze(
          fullTableName, cat, db, tbl, allCols, partsToAnalyze, writeIdString);
      LOG.debug("Columns to update are {} for all partitions; {} individual partitions."
          + " Existing only: {}, out of: {}", colsForAllParts, partsToAnalyze.size(),
          isExistingOnly, allCols);
      if (colsForAllParts == null && partsToAnalyze.isEmpty()) {
        return null; // No partitions need update.
      }
      if (colsForAllParts != null) {
        // We can update all partitions with a single analyze query.
        return Lists.newArrayList(new AnalyzeWork(
            fullTableName, null, buildPartColStr(table), colsForAllParts));
      }
      List<AnalyzeWork> result = new ArrayList<>(partsToAnalyze.size());
      for (Map.Entry<String, List<String>> e : partsToAnalyze.entrySet()) {
        LOG.debug("Adding analyze work for {}", e.getKey());
        result.add(new AnalyzeWork(fullTableName, e.getKey(), null, e.getValue()));
      }
      return result;
    }
  }
Whether to only update stats that already exist and are out of date.
ql/src/java/org/apache/hadoop/hive/ql/stats/StatsUpdaterThread.java