private OrcRawRecordMerger.KeyInterval findMinMaxKeys(
      OrcSplit orcSplit, Configuration conf,
      Reader.Options deleteEventReaderOptions) throws IOException {
    final boolean noDeleteDeltas = orcSplit.getDeltas().size() == 0;
    if(!HiveConf.getBoolVar(conf, ConfVars.FILTER_DELETE_EVENTS) || noDeleteDeltas) {
      LOG.debug("findMinMaxKeys() " + ConfVars.FILTER_DELETE_EVENTS + "=false");
      return new OrcRawRecordMerger.KeyInterval(null, null);
    }

    try (VectorizedOrcAcidRowBatchReader.ReaderData orcReaderData =
        getOrcReaderData(orcSplit.getPath(), conf, cacheTag, orcSplit.getFileKey())) {

      if(orcSplit.isOriginal()) {
        /**
         * Among originals we may have files with _copy_N suffix.  To properly
         * generate a synthetic ROW___ID for them we need
         * {@link OffsetAndBucketProperty} which could be an expensive computation
         * if there are lots of copy_N files for a given bucketId. But unless
         * there are delete events, we often don't need synthetic ROW__IDs at all.
         * Kind of chicken-and-egg - deal with this later.
         * See {@link OrcRawRecordMerger#discoverOriginalKeyBounds(Reader, int,
         * Reader.Options, Configuration, OrcRawRecordMerger.Options)}*/
        LOG.debug("findMinMaxKeys(original split)");

        return findOriginalMinMaxKeys(orcSplit, orcReaderData.orcTail, deleteEventReaderOptions);
      }

      List<StripeInformation> stripes = orcReaderData.orcTail.getStripes();
      final long splitStart = orcSplit.getStart();
      final long splitEnd = splitStart + orcSplit.getLength();
      int firstStripeIndex = -1;
      int lastStripeIndex = -1;
      for(int i = 0; i < stripes.size(); i++) {
        StripeInformation stripe = stripes.get(i);
        long stripeEnd = stripe.getOffset() + stripe.getLength();
        if(firstStripeIndex == -1 && stripe.getOffset() >= splitStart) {
          firstStripeIndex = i;
        }
        if(lastStripeIndex == -1 && splitEnd <= stripeEnd) {
          lastStripeIndex = i;
        }
      }
      if(lastStripeIndex == -1) {
        //split goes to the EOF which is > end of stripe since file has a footer
        assert stripes.get(stripes.size() - 1).getOffset() +
            stripes.get(stripes.size() - 1).getLength() < splitEnd;
        lastStripeIndex = stripes.size() - 1;
      }

      if (firstStripeIndex > lastStripeIndex || firstStripeIndex == -1) {
        /**
         * If the firstStripeIndex was set after the lastStripeIndex the split lies entirely within a single stripe.
         * In case the split lies entirely within the last stripe, the firstStripeIndex will never be found, hence the
         * second condition.
         * In this case, the reader for this split will not read any data.
         * See {@link org.apache.orc.impl.RecordReaderImpl#RecordReaderImpl
         * Create a KeyInterval such that no delete delta records are loaded into memory in the deleteEventRegistry.
         */

        long minRowId = 1;
        long maxRowId = 0;
        int minBucketProp = 1;
        int maxBucketProp = 0;

        OrcRawRecordMerger.KeyInterval keyIntervalTmp =
            new OrcRawRecordMerger.KeyInterval(new RecordIdentifier(1, minBucketProp, minRowId),
            new RecordIdentifier(0, maxBucketProp, maxRowId));

        setSARG(keyIntervalTmp, deleteEventReaderOptions, minBucketProp, maxBucketProp,
            minRowId, maxRowId);
        LOG.info("findMinMaxKeys(): " + keyIntervalTmp +
            " stripes(" + firstStripeIndex + "," + lastStripeIndex + ")");

        return keyIntervalTmp;
      }

      if(firstStripeIndex == -1 || lastStripeIndex == -1) {
        //this should not happen but... if we don't know which stripe(s) are
        //involved we can't figure out min/max bounds
        LOG.warn("Could not find stripe (" + firstStripeIndex + "," +
            lastStripeIndex + ")");
        return new OrcRawRecordMerger.KeyInterval(null, null);
      }
      RecordIdentifier[] keyIndex = OrcRecordUpdater.parseKeyIndex(orcReaderData.orcTail);

      if(keyIndex == null) {
        LOG.warn("Could not find keyIndex (" + firstStripeIndex + "," +
            lastStripeIndex + "," + stripes.size() + ")");
      }

      if(keyIndex != null && keyIndex.length != stripes.size()) {
        LOG.warn("keyIndex length doesn't match (" +
            firstStripeIndex + "," + lastStripeIndex + "," + stripes.size() +
            "," + keyIndex.length + ")");
        return new OrcRawRecordMerger.KeyInterval(null, null);
      }
      /**
       * If {@link OrcConf.ROW_INDEX_STRIDE} is set to 0 all column stats on
       * ORC file are disabled though objects for them exist but and have
       * min/max set to MIN_LONG/MAX_LONG so we only use column stats if they
       * are actually computed.  Streaming ingest used to set it 0 and Minor
       * compaction so there are lots of legacy files with no (rather, bad)
       * column stats*/
      boolean columnStatsPresent = orcReaderData.orcTail.getFooter().getRowIndexStride() > 0;
      if(!columnStatsPresent) {
        LOG.debug("findMinMaxKeys() No ORC column stats");
      }

      List<StripeStatistics> stats = orcReaderData.reader.getVariantStripeStatistics(null);
      assert stripes.size() == stats.size() : "str.s=" + stripes.size() +
          " sta.s=" + stats.size();

      RecordIdentifier minKey = null;
      if(firstStripeIndex > 0 && keyIndex != null) {
        //valid keys are strictly > than this key
        minKey = keyIndex[firstStripeIndex - 1];
        //add 1 to make comparison >= to match the case of 0th stripe
        minKey.setRowId(minKey.getRowId() + 1);
      }
      else {
        if(columnStatsPresent) {
          minKey = getKeyInterval(stats.get(firstStripeIndex).getColumnStatistics()).getMinKey();
        }
      }

      RecordIdentifier maxKey = null;

      if (keyIndex != null) {
        maxKey = keyIndex[lastStripeIndex];
      } else {
        if(columnStatsPresent) {
          maxKey = getKeyInterval(stats.get(lastStripeIndex).getColumnStatistics()).getMaxKey();
        }
      }
      OrcRawRecordMerger.KeyInterval keyInterval =
          new OrcRawRecordMerger.KeyInterval(minKey, maxKey);
      LOG.info("findMinMaxKeys(): " + keyInterval +
          " stripes(" + firstStripeIndex + "," + lastStripeIndex + ")");

      long minBucketProp = Long.MAX_VALUE, maxBucketProp = Long.MIN_VALUE;
      long minRowId = Long.MAX_VALUE, maxRowId = Long.MIN_VALUE;
      if(columnStatsPresent) {
        /**
         * figure out min/max bucket, rowid for push down.  This is different from
         * min/max ROW__ID because ROW__ID comparison uses dictionary order on two
         * tuples (a,b,c), but PPD can only do
         * (a between (x,y) and b between(x1,y1) and c between(x2,y2))
         * Consider:
         * (0,536936448,0), (0,536936448,2), (10000001,536936448,0)
         * 1st is min ROW_ID, 3r is max ROW_ID
         * and Delete events (0,536936448,2),....,(10000001,536936448,1000000)
         * So PPD based on min/max ROW_ID would have 0<= rowId <=0 which will
         * miss this delete event.  But we still want PPD to filter out data if
         * possible.
         *
         * So use stripe stats to find proper min/max for bucketProp and rowId
         * writeId is the same in both cases
         */
        for(int i = firstStripeIndex; i <= lastStripeIndex; i++) {
          OrcRawRecordMerger.KeyInterval key = getKeyInterval(stats.get(i).getColumnStatistics());
          if(key.getMinKey().getBucketProperty() < minBucketProp) {
            minBucketProp = key.getMinKey().getBucketProperty();
          }
          if(key.getMaxKey().getBucketProperty() > maxBucketProp) {
            maxBucketProp = key.getMaxKey().getBucketProperty();
          }
          if(key.getMinKey().getRowId() < minRowId) {
            minRowId = key.getMinKey().getRowId();
          }
          if(key.getMaxKey().getRowId() > maxRowId) {
            maxRowId = key.getMaxKey().getRowId();
          }
        }
      }
      if(minBucketProp == Long.MAX_VALUE) minBucketProp = Long.MIN_VALUE;
      if(maxBucketProp == Long.MIN_VALUE) maxBucketProp = Long.MAX_VALUE;
      if(minRowId == Long.MAX_VALUE) minRowId = Long.MIN_VALUE;
      if(maxRowId == Long.MIN_VALUE) maxRowId = Long.MAX_VALUE;

      setSARG(keyInterval, deleteEventReaderOptions, minBucketProp, maxBucketProp,
          minRowId, maxRowId);

      return keyInterval;
    }
  }
A given ORC reader will always process one or more whole stripes but the
split boundaries may not line up with stripe boundaries if the InputFormat
doesn't understand ORC specifics. So first we need to figure out which
stripe(s) we are reading.

Suppose txn1 writes 100K rows
and txn2 writes 100 rows so we have events
{1,0,0}....{1,0,100K},{2,0,0}...{2,0,100} in 2 files
After compaction we may have 2 stripes
{1,0,0}...{1,0,90K},{1,0,90001}...{2,0,100}

Now suppose there is a delete stmt that deletes every row.  So when we load
the 2nd stripe, if we just look at stripe {@link ColumnStatistics},
minKey={1,0,100} and maxKey={2,0,90001}, all but the 1st 100 delete events
will get loaded.  But with {@link OrcRecordUpdater#ACID_KEY_INDEX_NAME},
minKey={1,0,90001} and maxKey={2,0,100} so we only load about 10K deletes.

Also, even with Query Based compactor (once we have it), FileSinkOperator
uses OrcRecordWriter to write to file, so we should have the
hive.acid.index in place.

If reading the 1st stripe, we don't have the start event, so we'll get it
from stats, which will strictly speaking be accurate only wrt writeId and
bucket but that is good enough.

@return empty <code>KeyInterval</code> if KeyInterval could not be
determined
ql/src/java/org/apache/hadoop/hive/ql/io/orc/VectorizedOrcAcidRowBatchReader.java