private boolean nextRowBatch() throws SQLException {
    if (isClosed) {
      throw new SQLException("Resultset is closed");
    }
    if ((maxRows > 0 && rowsFetched >= maxRows) || emptyResultSet || fetchDone) {
      return false;
    }
    if (checkOperationStatus) {
      ((HiveStatement) statement).waitForOperationToComplete();
      checkOperationStatus = false;
    }

// TODO: Could support pool with maxRows by bounding results instead
    if (rowsFetched < fetchSize || fetchThreads == 0 || maxRows > 0) {
      try {
        int fetchSizeBounded = fetchSize;
        if (maxRows > 0 && rowsFetched + fetchSize > maxRows) {
          fetchSizeBounded = maxRows - (int)rowsFetched;
        }
        TFetchResultsReq fetchReq = new TFetchResultsReq(stmtHandle,
            orientation, fetchSizeBounded);
        TFetchResultsResp fetchResp = client.FetchResults(fetchReq);
        Utils.verifySuccessWithInfo(fetchResp.getStatus());
        TRowSet results = fetchResp.getResults();
        if (results.getStartRowOffset() > 0) {
          hasStartRow.set(true);
        }
        fetchedRows = RowSetFactory.create(results, protocol);
        fetchDone = !fetchResp.isHasMoreRows() && fetchedRows.numRows() == 0;
        if (fetchDone) {
          gotLastBatch.set(true);
        }
        fetchedRows = RowSetFactory.create(results, protocol);
        if (useConvertedResultSet) {
          fetchedRows = new ConvertedResultSet(fetchedRows, getSchema());
        }

        nextStartRow.set(results.getStartRowOffset() + 1 + fetchedRows.numRows());
      } catch (TException ex) {
        throw new SQLException("Error retrieving next row", ex);
      }
    } else {
      if (!gotLastBatch.get()) {
        if (pool == null) {
          pool = Executors.newFixedThreadPool(fetchThreads);
        }
        // Add another thread on each row batch up to the limit
        if (threadsStarted < (hasStartRow.get() ? fetchThreads : 1)) {
          final boolean useMainClient = (threadsStarted == 0);
          threadsStarted++;
          pool.execute(()-> {
            LOG.debug("Started thread {}", Thread.currentThread().getName());

            TCLIService.Iface fetchClient = null;
            HiveConnection threadConn = null;
            long startTime = System.nanoTime();

            while (!gotLastBatch.get() && !poolDone.get()) {
              if (threadConn != connection) {
                long endTime = System.nanoTime();
                // Re-open cloned connections every 5 sec to avoid starvation
                if (endTime - startTime > 5000000000L) {
                  closeConn(threadConn);
                  threadConn = null;
                  startTime = endTime;
                }
              }
              if (threadConn == null) {
                if (useMainClient) {
                  threadConn = (HiveConnection)connection;
                  fetchClient = client;
                } else {
                  try {
                    threadConn = new HiveConnection((HiveConnection)connection);
                    fetchClient = threadConn.getClient();
                  } catch (SQLException e) {
                    LOG.debug("Multi-stream connection error {}", e.toString());
                    return;
                  }
                }
              }
              FetchResult result = new FetchResult();
              try {
                final TFetchResultsReq fetchReq = new TFetchResultsReq(
                    stmtHandle, orientation, fetchSize);
                TFetchResultsResp fetchResp = fetchClient.FetchResults(fetchReq);
                Utils.verifySuccessWithInfo(fetchResp.getStatus());
                TRowSet results = fetchResp.getResults();
                if (results.getStartRowOffset() > 0) {
                  hasStartRow.set(true);
                }
                result.fetchedRows = RowSetFactory.create(results, protocol);
                result.numRows = result.fetchedRows.numRows();
                boolean hasMoreRows = result.numRows > 0 || fetchResp.isHasMoreRows();
                if (!hasMoreRows) {
                  gotLastBatch.set(true);
                }
                result.hasMoreRows = hasMoreRows;
                result.fetchedRows = RowSetFactory.create(results, protocol);
                if (useConvertedResultSet) {
                  result.fetchedRows =
                      new ConvertedResultSet(result.fetchedRows, getSchema());
                }
                result.startRow = results.getStartRowOffset() + 1;
                if (hasStartRow.get() && result.startRow < nextStartRow.get()) {
                  throw new SQLException("Unexpected row offset");
                }
              } catch (Exception e) {
                result.ex = e;
              }

              try {
                // Wait for earlier row sets to be added to the queue
                synchronized(nextStartRow) {
                  if (!poolDone.get()) {
                    if (result.ex == null) {
                      if (hasStartRow.get()) {
                        while (nextStartRow.get() != result.startRow) {
                          nextStartRow.wait();
                        }
                        nextStartRow.set(result.startRow + result.numRows);
                      }
                      poolDone.set(!result.hasMoreRows);
                    } else {
                      poolDone.set(true);
                    }
                    resultQueue.put(result);
                    if (hasStartRow.get()) {
                      nextStartRow.notifyAll();
                    }
                  }
                }
              } catch (InterruptedException e) {
                interruptException.set(e);
                break;
              }
            }
            if (threadConn != connection) {
              closeConn(threadConn);
            }
          });
        }
      }
      try {
        if (interruptException.get() != null) {
          throw interruptException.get();
        }
        FetchResult result = resultQueue.take();
        fetchDone = !result.hasMoreRows;
        if (result.ex != null) {
          shutdownPool();
          throw new SQLException(result.ex);
        }
        fetchedRows = result.fetchedRows;
      } catch (InterruptedException e) {
        throw new SQLException(e);
      }
    }

    orientation = TFetchOrientation.FETCH_NEXT;
    fetchedRowsItr = fetchedRows.iterator();

    return fetchedRowsItr.hasNext();
  }
Set the specified schema to the resultset
@param colNames
@param colTypes
jdbc/src/java/org/apache/hive/jdbc/HiveQueryResultSet.java