public List<Object[]> executeStatement(String statement) {
    Preconditions.checkState(session.get() != null,
            "You have to start TestHiveShell and open a session first, before running a query.");
    try {
      OperationHandle handle = client.executeStatement(session.get().getSessionHandle(), statement,
          Collections.emptyMap());
      List<Object[]> resultSet = Lists.newArrayList();
      if (handle.hasResultSet()) {
        RowSet rowSet;
        // keep fetching results until we can
        while ((rowSet = client.fetchResults(handle)) != null && rowSet.numRows() > 0) {
          for (Object[] row : rowSet) {
            resultSet.add(row.clone());
          }
        }
      }
      return resultSet;
    } catch (HiveSQLException e) {
      throw new IllegalArgumentException("Failed to execute Hive query '" + statement + "': " + e.getMessage(), e);
    }
  }
Test class for running HiveQL queries, essentially acting like a Beeline shell in tests.

It spins up both an HS2 and a Metastore instance to work with. The shell will only accept
queries if it has been previously initialized via {@link #start()}, and a session has been opened via
{@link #openSession()}. Prior to calling {@link #start()}, the shell should first be configured with props that apply
across all test cases by calling {@link #setHiveConfValue(String, String)} ()}. On the other hand, session-level conf
can be applied anytime via {@link #setHiveSessionValue(String, String)} ()}, once we've opened an active session.
iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveShell.java