private void performDataRead() throws IOException {
    LlapIoImpl.LOG.info("Processing data for file {}: {}", fileKey, split.getPath());
    if (processStop()) {
      return;
    }
    counters.setDesc(QueryFragmentCounters.Desc.TABLE, cacheTag.getTableName());
    counters.setDesc(QueryFragmentCounters.Desc.FILE, split.getPath()
        + (fileKey == null ? "" : " (" + fileKey + ")"));

    validateFileMetadata();

    // 2. Determine which stripes to read based on the split.
    determineStripesToRead();

    if (stripeRgs.length == 0) {
      return;
    }
    counters.setDesc(QueryFragmentCounters.Desc.STRIPES,
        stripeIxFrom + "," + stripeRgs.length);

    // 3. Apply SARG if needed, and otherwise determine what RGs to read.
    int stride = fileMetadata.getRowIndexStride();
    ArrayList<OrcStripeMetadata> stripeMetadatas = null;
    if (sarg != null && stride != 0) {
      // TODO: move this to a common method
      // Note: this gets IDs by name, so we assume indices don't need to be adjusted for ACID.
      int[] filterColumns = RecordReaderImpl.mapSargColumnsToOrcInternalColIdx(
        sarg.getLeaves(), evolution);
      // included will not be null, row options will fill the array with trues if null
      sargColumns = new boolean[evolution.getFileSchema().getMaximumId() + 1];
      for (int i : filterColumns) {
        // filter columns may have -1 as index which could be partition column in SARG.
        // TODO: should this then be >=?
        if (i > 0) {
          sargColumns[i] = true;
        }
      }

      // If SARG is present, get relevant stripe metadata from cache or readers.
      stripeMetadatas = readStripesMetadata(fileIncludes, sargColumns);
    }

    // Now, apply SARG if any; w/o sarg, this will just initialize stripeRgs.
    boolean hasData = determineRgsToRead(stride, stripeMetadatas);
    if (!hasData) {
      return; // No data to read.
    }

    if (processStop()) {
      return;
    }

    // 4. Create encoded data reader.
    ensureDataReader();

    // 6. Read data.
    // TODO: I/O threadpool could be here - one thread per stripe; for now, linear.
    boolean hasFileId = this.fileKey != null;
    OrcBatchKey stripeKey = hasFileId ? new OrcBatchKey(fileKey, -1, 0) : null;
    pathCache.touch(fileKey, split.getPath().toUri().toString());
    for (int stripeIxMod = 0; stripeIxMod < stripeRgs.length; ++stripeIxMod) {
      if (processStop()) {
        return;
      }
      int stripeIx = stripeIxFrom + stripeIxMod;
      boolean[] rgs = null;
      OrcStripeMetadata stripeMetadata = null;
      StripeInformation si;
      si = fileMetadata.getStripes().get(stripeIx);
      LlapIoImpl.ORC_LOGGER.trace("Reading stripe {}: {}, {}", stripeIx, si.getOffset(),
          si.getLength());
      trace.logReadingStripe(stripeIx, si.getOffset(), si.getLength());
      rgs = stripeRgs[stripeIxMod];
      if (LlapIoImpl.ORC_LOGGER.isTraceEnabled()) {
        LlapIoImpl.ORC_LOGGER.trace("stripeRgs[{}]: {}", stripeIxMod, Arrays.toString(rgs));
      }
      // We assume that NO_RGS value is only set from SARG filter and for all columns;
      // intermediate changes for individual columns will unset values in the array.
      // Skip this case for 0-column read. We could probably special-case it just like we do
      // in EncodedReaderImpl, but for now it's not that important.
      if (rgs == RecordReaderImpl.SargApplier.READ_NO_RGS) continue;

      // 6.2. Ensure we have stripe metadata. We might have read it before for RG filtering.
      if (stripeMetadatas != null) {
        stripeMetadata = stripeMetadatas.get(stripeIxMod);
      } else {
        stripeKey.stripeIx = stripeIx;
        OrcProto.StripeFooter footer = getStripeFooterFromCacheOrDisk(si, stripeKey);
        stripeMetadata = createOrcStripeMetadataObject(
            stripeIx, si, footer, fileIncludes, sargColumns);
        ensureDataReader();
        stripeReader.readIndexStreams(stripeMetadata.getIndex(),
            si, footer.getStreamsList(), fileIncludes, sargColumns);
        consumer.setStripeMetadata(stripeMetadata);
      }
      if (processStop()) {
        return;
      }

      // 5.2. Finally, hand off to the stripe reader to produce the data.
      //      This is a sync call that will feed data to the consumer.
      // TODO: readEncodedColumns is not supposed to throw; errors should be propagated thru
      // consumer. It is potentially holding locked buffers, and must perform its own cleanup.
      // Also, currently readEncodedColumns is not stoppable. The consumer will discard the
      // data it receives for one stripe. We could probably interrupt it, if it checked that.
      stripeReader.readEncodedColumns(stripeIx, si, stripeMetadata.getRowIndexes(),
          stripeMetadata.getEncodings(), stripeMetadata.getStreams(), fileIncludes,
          rgs, consumer);
    }

    LlapIoImpl.LOG.trace("done processing {}", split);
  }
stripeRgs[stripeIx'] => boolean array (could be a bitmask) of rg-s that need to be read.
Contains only stripes that are read, and only columns included. null => read all RGs.
llap-server/src/java/org/apache/hadoop/hive/llap/io/encoded/OrcEncodedDataReader.java