@Override
  protected final void prepare(ExecuteContext ctx) throws SQLException {

    // [#1296] These dialects do not implement FOR UPDATE. But the same
    // effect can be achieved using ResultSet.CONCUR_UPDATABLE
    if (isForUpdate() && asList(CUBRID, SQLSERVER).contains(ctx.getDialect())) {
      ctx.statement(
          ctx.getConnection().prepareStatement(ctx.sql(), TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE));
    }

    // Regular behaviour
    else {
      ctx.statement(ctx.getConnection().prepareStatement(ctx.sql()));
    }

    // [#1263] Allow for negative fetch sizes to support some non-standard
    // MySQL feature, where Integer.MIN_VALUE is used
    if (size != 0) {
      if (log.isDebugEnabled()) log.debug("Setting fetch size", size);

      ctx.statement().setFetchSize(size);
    }

    // [#1854] Set the max number of rows for this result query
    if (maxRows != 0) {
      ctx.statement().setMaxRows(maxRows);
    }
  }
  @Override
  protected final int execute(ExecuteContext ctx, ExecuteListener listener) throws SQLException {
    Connection connection = ctx.getConnection();
    boolean autoCommit = false;

    // [#706] Postgres requires two separate queries running in the same
    // transaction to be executed when fetching refcursor types
    if (ctx.getDialect() == SQLDialect.POSTGRES && isSelectingRefCursor()) {
      autoCommit = connection.getAutoCommit();

      if (autoCommit) {
        if (log.isDebugEnabled()) log.debug("Unsetting auto-commit", false);

        connection.setAutoCommit(false);
      }
    }

    try {
      listener.executeStart(ctx);

      // JTDS doesn't seem to implement PreparedStatement.execute()
      // correctly, at least not for sp_help
      if (ctx.getDialect() == ASE) {
        ctx.resultSet(ctx.statement().executeQuery());
      }

      // [#1232] Avoid executeQuery() in order to handle queries that may
      // not return a ResultSet, e.g. SQLite's pragma foreign_key_list(table)
      else if (ctx.statement().execute()) {
        ctx.resultSet(ctx.statement().getResultSet());
      }

      listener.executeEnd(ctx);

      // Fetch a single result set
      if (!many) {
        if (ctx.resultSet() != null) {
          FieldList fields = new FieldList(getFields(ctx.resultSet().getMetaData()));
          cursor = new CursorImpl<R>(ctx, listener, fields, getRecordType());

          if (!lazy) {
            result = cursor.fetch();
            cursor = null;
          }
        } else {
          result = new ResultImpl<R>(ctx, new FieldList());
        }
      }

      // Fetch several result sets
      else {
        results = new ArrayList<Result<Record>>();
        boolean anyResults = false;

        while (ctx.resultSet() != null) {
          anyResults = true;

          FieldProvider fields = new MetaDataFieldProvider(ctx, ctx.resultSet().getMetaData());
          Cursor<Record> c = new CursorImpl<Record>(ctx, listener, fields, true);
          results.add(c.fetch());

          if (ctx.statement().getMoreResults()) {
            ctx.resultSet(ctx.statement().getResultSet());
          } else {
            ctx.resultSet(null);
          }
        }

        // Call this only when there was at least one ResultSet.
        // Otherwise, this call is not supported by ojdbc...
        if (anyResults) {
          ctx.statement().getMoreResults(Statement.CLOSE_ALL_RESULTS);
        }

        ctx.statement().close();
      }
    } finally {
      if (autoCommit) {
        if (log.isDebugEnabled()) log.debug("Resetting auto-commit", autoCommit);

        connection.setAutoCommit(autoCommit);
      }
    }

    return result != null ? result.size() : 0;
  }