private List<String> findPartitionsToAnalyze(TableName fullTableName, String cat, String db,
      String tbl, List<String> allCols, Map<String, List<String>> partsToAnalyze,
      String writeIdString) throws MetaException, NoSuchObjectException {
    // TODO: ideally when col-stats-accurate stuff is stored in some sane structure, this should
    //       retrieve partsToUpdate in a single query; no checking partition params in java.
    List<String> partNames = null;
    Map<String, List<String>> colsPerPartition = null;
    boolean isAllParts = true;
    if (isExistingOnly) {
      // Make sure the number of partitions we get, and the number of stats objects, is consistent.
      rs.openTransaction();
      boolean isOk = false;
      try {
        colsPerPartition = rs.getPartitionColsWithStats(cat, db, tbl);
        partNames = Lists.newArrayList(colsPerPartition.keySet());
        int partitionCount = rs.getNumPartitionsByFilter(cat, db, tbl, "");
        isAllParts = partitionCount == partNames.size();
        isOk = true;
      } finally {
        if (isOk) {
          rs.commitTransaction();
        } else {
          rs.rollbackTransaction();
        }
      }
    } else {
      partNames = rs.listPartitionNames(cat, db, tbl, (short) -1);
      isAllParts = true;
    }
    Table t = rs.getTable(cat, db, tbl);
    List<Partition> currentBatch = null;
    int nextBatchStart = 0, nextIxInBatch = -1, currentBatchStart = 0;
    List<String> colsToUpdateForAll = null;
    while (true) {
      if (currentBatch == null || nextIxInBatch == currentBatch.size()) {
        if (nextBatchStart >= partNames.size()) {
          break;
        }
        int nextBatchEnd = Math.min(partNames.size(), nextBatchStart + this.batchSize);
        List<String> currentNames = partNames.subList(nextBatchStart, nextBatchEnd);
        currentBatchStart = nextBatchStart;
        nextBatchStart = nextBatchEnd;

        GetProjectionsSpec projectionsSpec = new GetPartitionProjectionsSpecBuilder()
            .addProjectFieldList(Arrays.asList("values","parameters","writeId")).build();
        GetPartitionsFilterSpec partitionsFilterSpec = new GetPartitionsFilterSpec();
        partitionsFilterSpec.setFilters(currentNames);
        partitionsFilterSpec.setFilterMode(PartitionFilterMode.BY_NAMES);

        try {
          currentBatch = rs.getPartitionSpecsByFilterAndProjection(t, projectionsSpec, partitionsFilterSpec);
        } catch (NoSuchObjectException e) {
          LOG.error("Failed to get partitions for " + fullTableName + ", skipping some partitions", e);
          currentBatch = null;
          continue;
        }
        nextIxInBatch = 0;
      }
      int currentIxInBatch = nextIxInBatch++;
      Partition part = currentBatch.get(currentIxInBatch);
      String partName = Warehouse.makePartName(t.getPartitionKeys(), part.getValues());
      LOG.debug("Processing partition ({} in batch), {}", currentIxInBatch, partName);

      // Skip the partitions in progress, and the ones for which stats update is disabled.
      // We could filter the skipped partititons out as part of the initial names query,
      // but we assume it's extremely rare for individual partitions.
      Map<String, String> params = part.getParameters();
      String skipParam = params.get(SKIP_STATS_AUTOUPDATE_PROPERTY);
      if (isAnalyzePartInProgress(fullTableName, partName) || "true".equalsIgnoreCase(skipParam)) {
        if (isAllParts) {
          addPreviousPartitions(t, partNames, currentBatchStart, currentBatch, currentIxInBatch,
              colsToUpdateForAll, partsToAnalyze);
        }
        isAllParts = false;
        continue;
      }

      // Find which columns we need to update for this partition, if any.
      List<String> colsToMaybeUpdate = allCols;
      if (isExistingOnly) {
        colsToMaybeUpdate = colsPerPartition.get(partName);
        Collections.sort(colsToMaybeUpdate);
      }
      List<String> colsToUpdate = getAnyStatsToUpdate(db, tbl, colsToMaybeUpdate, params,
          writeIdString == null ? -1 : part.getWriteId(), writeIdString);

      LOG.debug("Updating {} based on {} and {}", colsToUpdate, colsToMaybeUpdate, params);

      if (colsToUpdate == null || colsToUpdate.isEmpty()) {
        if (isAllParts) {
          addPreviousPartitions(t, partNames, currentBatchStart, currentBatch, currentIxInBatch,
              colsToUpdateForAll, partsToAnalyze);
        }
        isAllParts = false;
        continue;
      }

      // If issuing a query for all partitions, verify that we need update the same columns.
      // TODO: for non columnar we don't need to do this... might as well update all stats.
      if (isAllParts) {
        List<String> newCols = verifySameColumnsForAllParts(colsToUpdateForAll, colsToUpdate);
        if (newCols == null) {
          isAllParts = false;
          addPreviousPartitions(t, partNames, currentBatchStart, currentBatch, currentIxInBatch,
              colsToUpdateForAll, partsToAnalyze);
        } else if (colsToUpdateForAll == null) {
          colsToUpdateForAll = newCols;
        }
      }

      if (!isAllParts) {
        LOG.trace("Adding {}, {}", partName, colsToUpdate);
        partsToAnalyze.put(partName, colsToUpdate);
      }
    }
    return isAllParts ? colsToUpdateForAll : null;
  }
Whether to only update stats that already exist and are out of date.
ql/src/java/org/apache/hadoop/hive/ql/stats/StatsUpdaterThread.java