private MergedColumnStatsForPartitions mergeColStatsForPartitions(String catName, String dbName, String tblName,
      List<String> partNames, List<String> colNames, SharedCache sharedCache, StatsType type, String writeIdList)
      throws MetaException {
    final boolean useDensityFunctionForNDVEstimation =
        MetastoreConf.getBoolVar(getConf(), ConfVars.STATS_NDV_DENSITY_FUNCTION);
    final double ndvTuner = MetastoreConf.getDoubleVar(getConf(), ConfVars.STATS_NDV_TUNER);
    Map<ColumnStatsAggregator, List<ColStatsObjWithSourceInfo>> colStatsMap = new HashMap<>();
    long partsFound = partNames.size();
    Map<List<String>, Long> partNameToWriteId = writeIdList != null ? new HashMap<>() : null;
    for (String colName : colNames) {
      long partsFoundForColumn = 0;
      ColumnStatsAggregator colStatsAggregator = null;
      List<ColStatsObjWithSourceInfo> colStatsWithPartInfoList = new ArrayList<>();
      for (String partName : partNames) {
        List<String> partValue = partNameToVals(partName);
        // There are three possible result from getPartitionColStatsFromCache.
        // 1. The partition has valid stats and thus colStatsWriteId returned is valid non-null value
        // 2. Partition stat is missing from cache and thus colStatsWriteId returned is non-null but colstat
        //    info in it is null. In this case we just ignore the partition from aggregate calculation to keep
        //    the behavior same as object store.
        // 3. Partition is missing or its stat is updated by live(not yet committed) or aborted txn. In this case,
        //    colStatsWriteId is null. Thus null is returned to keep the behavior same as object store.
        SharedCache.ColumStatsWithWriteId colStatsWriteId =
            sharedCache.getPartitionColStatsFromCache(catName, dbName, tblName, partValue, colName, writeIdList);
        if (colStatsWriteId == null) {
          return null;
        }
        if (colStatsWriteId.getColumnStatisticsObj() != null) {
          ColumnStatisticsObj colStatsForPart = colStatsWriteId.getColumnStatisticsObj();
          if (partNameToWriteId != null) {
            partNameToWriteId.put(partValue, colStatsWriteId.getWriteId());
          }
          ColStatsObjWithSourceInfo colStatsWithPartInfo =
              new ColStatsObjWithSourceInfo(colStatsForPart, catName, dbName, tblName, partName);
          colStatsWithPartInfoList.add(colStatsWithPartInfo);
          if (colStatsAggregator == null) {
            colStatsAggregator = ColumnStatsAggregatorFactory
                .getColumnStatsAggregator(colStatsForPart.getStatsData().getSetField(),
                    useDensityFunctionForNDVEstimation, ndvTuner);
          }
          partsFoundForColumn++;
        } else {
          LOG.debug("Stats not found in CachedStore for: dbName={} tblName={} partName={} colName={}", dbName, tblName,
              partName, colName);
        }
      }
      if (colStatsWithPartInfoList.size() > 0) {
        colStatsMap.put(colStatsAggregator, colStatsWithPartInfoList);
      }
      // set partsFound to the min(partsFoundForColumn) for all columns. partsFound is the number of partitions, for
      // which stats for all columns are present in the cache.
      if (partsFoundForColumn < partsFound) {
        partsFound = partsFoundForColumn;
      }
      if (colStatsMap.size() < 1) {
        LOG.debug("No stats data found for: dbName={} tblName= {} partNames= {} colNames= ", dbName, tblName, partNames,
            colNames);
        // TODO: If we don't find any stats then most likely we should return null. Returning an empty object will not
        // trigger the lookup in the raw store and we will end up with missing stats.
        return new MergedColumnStatsForPartitions(new ArrayList<ColumnStatisticsObj>(), 0);
      }
    }
    // Note that enableBitVector does not apply here because ColumnStatisticsObj
    // itself will tell whether bitvector is null or not and aggr logic can automatically apply.
    List<ColumnStatisticsObj> colAggrStats = MetaStoreServerUtils
        .aggrPartitionStats(colStatsMap, partNames, partsFound == partNames.size(), useDensityFunctionForNDVEstimation,
            ndvTuner);

    if (canUseEvents) {
      if (type == StatsType.ALL) {
        sharedCache.refreshAggregateStatsInCache(StringUtils.normalizeIdentifier(catName),
            StringUtils.normalizeIdentifier(dbName), StringUtils.normalizeIdentifier(tblName),
            new AggrStats(colAggrStats, partsFound), null, partNameToWriteId);
      } else if (type == StatsType.ALLBUTDEFAULT) {
        sharedCache.refreshAggregateStatsInCache(StringUtils.normalizeIdentifier(catName),
            StringUtils.normalizeIdentifier(dbName), StringUtils.normalizeIdentifier(tblName), null,
            new AggrStats(colAggrStats, partsFound), partNameToWriteId);
      }
    }
    return new MergedColumnStatsForPartitions(colAggrStats, partsFound);
  }
getPartitionSpecsByFilterAndProjection interface is currently non-cacheable.
standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/cache/CachedStore.java