/**
   * Method to execute a PreparedStatement query, and return the ResultSet. Prints logging
   * information about timings.
   *
   * @param conn The connection (required since the one on PreparedStatement is not always the same
   *     so we can't use it)
   * @param stmt The statement text
   * @param ps The Prepared Statement
   * @return The ResultSet from the query
   * @throws SQLException Thrown if an error occurs
   */
  public ResultSet executeStatementQuery(
      ExecutionContext ec, ManagedConnection conn, String stmt, PreparedStatement ps)
      throws SQLException {
    if (supportsBatching) {
      ConnectionStatementState state = getConnectionStatementState(conn);
      if (state != null) {
        if (state.processable) {
          // Current batch statement is processable now so lets just process it before processing
          // our query
          processConnectionStatement(conn);
        } else {
          // Current wait statement is not processable now so leave it in wait state
          if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled()) {
            NucleusLogger.DATASTORE_RETRIEVE.debug(LOCALISER.msg("052106", state.stmtText, stmt));
          }
        }
      }
    }

    // Execute this query
    long startTime = System.currentTimeMillis();
    if (NucleusLogger.DATASTORE_NATIVE.isDebugEnabled()) {
      if (ps instanceof ParamLoggingPreparedStatement) {
        NucleusLogger.DATASTORE_NATIVE.debug(
            ((ParamLoggingPreparedStatement) ps).getStatementWithParamsReplaced());
      } else {
        NucleusLogger.DATASTORE_NATIVE.debug(stmt);
      }
    }

    ResultSet rs = ps.executeQuery();
    if (ec != null && ec.getStatistics() != null) {
      // Add to statistics
      ec.getStatistics().incrementNumReads();
    }

    ps.clearBatch();
    if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled()) {
      NucleusLogger.DATASTORE_RETRIEVE.debug(
          LOCALISER.msg("045000", (System.currentTimeMillis() - startTime)));
    }

    return rs;
  }
  /**
   * Method to execute a PreparedStatement (using PreparedStatement.execute()). Prints logging
   * information about timings.
   *
   * @param ec Execution Context
   * @param conn The connection (required since the one on PreparedStatement is not always the same
   *     so we can't use it)
   * @param stmt The statement text
   * @param ps The Prepared Statement
   * @return The numer of rows affected (as per PreparedStatement.execute)
   * @throws SQLException Thrown if an error occurs
   */
  public boolean executeStatement(
      ExecutionContext ec, ManagedConnection conn, String stmt, PreparedStatement ps)
      throws SQLException {
    if (supportsBatching) {
      // Check for a waiting batched statement that is ready for processing
      ConnectionStatementState state = getConnectionStatementState(conn);
      if (state != null && state.processable) {
        // Process the batch statement before returning our new query statement
        processConnectionStatement(conn);
      }
    }

    // Process the normal execute statement
    long startTime = System.currentTimeMillis();
    if (NucleusLogger.DATASTORE_NATIVE.isDebugEnabled()) {
      if (ps instanceof ParamLoggingPreparedStatement) {
        NucleusLogger.DATASTORE_NATIVE.debug(
            ((ParamLoggingPreparedStatement) ps).getStatementWithParamsReplaced());
      } else {
        NucleusLogger.DATASTORE_NATIVE.debug(stmt);
      }
    }

    boolean flag = ps.execute();
    if (ec != null && ec.getStatistics() != null) {
      // Add to statistics
      ec.getStatistics().incrementNumWrites();
    }

    ps.clearBatch();
    if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
      NucleusLogger.DATASTORE_PERSIST.debug(
          LOCALISER.msg(
              "045002",
              "" + (System.currentTimeMillis() - startTime),
              StringUtils.toJVMIDString(ps)));
    }

    return flag;
  }
  /**
   * Method to execute a PreparedStatement update. Prints logging information about timings.
   *
   * @param conn The connection (required since the one on PreparedStatement is not always the same
   *     so we cant use it)
   * @param stmt The statement text
   * @param ps The Prepared Statement
   * @param processNow Whether to process this statement now (only applies if is batched)
   * @return The numer of rows affected (as per PreparedStatement.executeUpdate)
   * @throws SQLException Thrown if an error occurs
   */
  public int[] executeStatementUpdate(
      ExecutionContext ec,
      ManagedConnection conn,
      String stmt,
      PreparedStatement ps,
      boolean processNow)
      throws SQLException {
    ConnectionStatementState state = getConnectionStatementState(conn);
    if (state != null) {
      if (state.stmt == ps) {
        // Mark as processable
        if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
          NucleusLogger.DATASTORE_PERSIST.debug(
              LOCALISER.msg("052104", state.stmtText, "" + state.batchSize));
        }
        state.processable = true;
        state.stmt.addBatch();

        if (processNow) {
          // Process the batch now
          state.closeStatementOnProcess =
              false; // user method has requested execution so they can close it themselves now
          return processConnectionStatement(conn);
        } else {
          // Leave processing til later
          return null;
        }
      } else {
        // There is a waiting batch yet it is a different statement, so process that one now since
        // we need
        // our statement executing
        processConnectionStatement(conn);
      }
    }

    // Process the normal update statement
    long startTime = System.currentTimeMillis();
    if (NucleusLogger.DATASTORE_NATIVE.isDebugEnabled()) {
      if (ps instanceof ParamLoggingPreparedStatement) {
        NucleusLogger.DATASTORE_NATIVE.debug(
            ((ParamLoggingPreparedStatement) ps).getStatementWithParamsReplaced());
      } else {
        NucleusLogger.DATASTORE_NATIVE.debug(stmt);
      }
    }

    int ind = ps.executeUpdate();
    if (ec != null && ec.getStatistics() != null) {
      // Add to statistics
      ec.getStatistics().incrementNumWrites();
    }

    ps.clearBatch();
    if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
      NucleusLogger.DATASTORE_PERSIST.debug(
          LOCALISER.msg(
              "045001",
              "" + (System.currentTimeMillis() - startTime),
              "" + ind,
              StringUtils.toJVMIDString(ps)));
    }

    return new int[] {ind};
  }