コード例 #1
0
  static {
    SqlInvariants.isSystemSchemaName(SqlInvariants.SYSTEM_SCHEMA);
    Charset.getDefaultInstance();
    Collation.getDefaultInstance();

    sessionAttributesMetaData = ResultMetaData.newResultMetaData(SessionInterface.INFO_LIMIT);

    for (int i = 0; i < Session.INFO_LIMIT; i++) {
      sessionAttributesMetaData.columns[i] = new ColumnBase(null, null, null, null);
    }

    sessionAttributesMetaData.columns[Session.INFO_ID].setType(Type.SQL_INTEGER);
    sessionAttributesMetaData.columns[Session.INFO_INTEGER].setType(Type.SQL_INTEGER);
    sessionAttributesMetaData.columns[Session.INFO_BOOLEAN].setType(Type.SQL_BOOLEAN);
    sessionAttributesMetaData.columns[Session.INFO_VARCHAR].setType(Type.SQL_VARCHAR);
    sessionAttributesMetaData.prepareData();
  }
コード例 #2
0
/**
 * The primary unit of communication between Connection, Server and Session objects.
 *
 * <p>An HSQLDB Result object encapsulates all requests (such as to alter or query session settings,
 * to allocate and execute statements, etc.) and all responses (such as exception indications,
 * update counts, result sets and result set metadata). It also implements the HSQL wire protocol
 * for comunicating all such requests and responses across the network. Uses a navigator for data.
 *
 * @author Campbell Boucher-Burnet (boucherb@users dot sourceforge.net)
 * @author Fred Toussi (fredt@users dot sourceforge.net)
 * @version 2.3.2
 * @since 1.9.0
 */
public class Result {

  public static final ResultMetaData sessionAttributesMetaData;

  static {
    SqlInvariants.isSystemSchemaName(SqlInvariants.SYSTEM_SCHEMA);
    Charset.getDefaultInstance();
    Collation.getDefaultInstance();

    sessionAttributesMetaData = ResultMetaData.newResultMetaData(SessionInterface.INFO_LIMIT);

    for (int i = 0; i < Session.INFO_LIMIT; i++) {
      sessionAttributesMetaData.columns[i] = new ColumnBase(null, null, null, null);
    }

    sessionAttributesMetaData.columns[Session.INFO_ID].setType(Type.SQL_INTEGER);
    sessionAttributesMetaData.columns[Session.INFO_INTEGER].setType(Type.SQL_INTEGER);
    sessionAttributesMetaData.columns[Session.INFO_BOOLEAN].setType(Type.SQL_BOOLEAN);
    sessionAttributesMetaData.columns[Session.INFO_VARCHAR].setType(Type.SQL_VARCHAR);
    sessionAttributesMetaData.prepareData();
  }

  private static final ResultMetaData emptyMeta = ResultMetaData.newResultMetaData(0);
  public static final Result emptyGeneratedResult = Result.newDataResult(emptyMeta);
  public static final Result updateZeroResult = newUpdateCountResult(0);
  public static final Result updateOneResult = newUpdateCountResult(1);

  // type of result
  public byte mode;

  // database ID
  int databaseID;

  // session ID
  long sessionID;

  // result id
  private long id;

  // database name for new connection
  private String databaseName;

  // user / password for new connection
  // error strings in error results
  private String mainString;
  private String subString;
  private String zoneString;

  // vendor error code
  int errorCode;

  // the exception if this is an error
  private HsqlException exception;

  // prepared statement id
  long statementID;

  // statement type based on whether it returns an update count or a result set
  // type of session info requested
  int statementReturnType;

  // max rows (out)
  // update count (in)
  // fetch part result count (in)
  // time zone seconds (connect)
  public int updateCount;

  // fetch size (in)
  private int fetchSize;

  // secondary result
  private Result chainedResult;

  //
  private int lobCount;
  ResultLob lobResults;

  /** A Result object's metadata */
  public ResultMetaData metaData;

  /** Additional meta data for parameters used in PREPARE_ACK results */
  public ResultMetaData parameterMetaData;

  /** Additional meta data for required generated columns */
  public ResultMetaData generatedMetaData;

  //
  public int rsProperties;

  //
  public int queryTimeout;

  //
  int generateKeys;

  // simple value for PSM, or parameter array
  public Object valueData;

  //
  public Statement statement;

  Result(int mode) {
    this.mode = (byte) mode;
  }

  public Result(int mode, int count) {
    this.mode = (byte) mode;
    updateCount = count;
  }

  public static Result newResult(RowSetNavigator nav) {

    Result result = new Result(ResultConstants.DATA);

    result.navigator = nav;

    return result;
  }

  public static Result newResult(int type) {

    RowSetNavigator navigator = null;
    Result result = null;

    switch (type) {
      case ResultConstants.CALL_RESPONSE:
      case ResultConstants.EXECUTE:
      case ResultConstants.UPDATE_RESULT:
        break;

      case ResultConstants.BATCHEXECUTE:
      case ResultConstants.BATCHEXECDIRECT:
        navigator = new RowSetNavigatorClient(4);
        break;

      case ResultConstants.SETSESSIONATTR:
      case ResultConstants.PARAM_METADATA:
        navigator = new RowSetNavigatorClient(1);
        break;

      case ResultConstants.BATCHEXECRESPONSE:
        navigator = new RowSetNavigatorClient(4);
        break;

      case ResultConstants.DATA:
      case ResultConstants.DATAHEAD:
      case ResultConstants.DATAROWS:
      case ResultConstants.GENERATED:
        break;

      case ResultConstants.LARGE_OBJECT_OP:
        throw Error.runtimeError(ErrorCode.U_S0500, "Result");
      default:
    }

    result = new Result(type);
    result.navigator = navigator;

    return result;
  }

  public static Result newResult(DataInput dataInput, RowInputBinary in)
      throws IOException, HsqlException {
    return newResult(null, dataInput.readByte(), dataInput, in);
  }

  public static Result newResult(Session session, int mode, DataInput dataInput, RowInputBinary in)
      throws IOException, HsqlException {

    try {
      if (mode == ResultConstants.LARGE_OBJECT_OP) {
        return ResultLob.newLob(dataInput, false);
      }

      Result result = newResult(session, dataInput, in, mode);

      return result;
    } catch (IOException e) {
      throw Error.error(ErrorCode.X_08000);
    }
  }

  public void readAdditionalResults(
      SessionInterface session, DataInputStream inputStream, RowInputBinary in)
      throws IOException, HsqlException {

    Result currentResult = this;

    setSession(session);

    while (true) {
      int addedResultMode = inputStream.readByte();

      if (addedResultMode == ResultConstants.NONE) {
        return;
      }

      currentResult = newResult(null, inputStream, in, addedResultMode);

      addChainedResult(currentResult);
    }
  }

  public void readLobResults(
      SessionInterface session, DataInputStream inputStream, RowInputBinary in)
      throws IOException, HsqlException {

    Result currentResult = this;
    boolean hasLob = false;

    setSession(session);

    while (true) {
      int addedResultMode = inputStream.readByte();

      if (addedResultMode == ResultConstants.LARGE_OBJECT_OP) {
        ResultLob resultLob = ResultLob.newLob(inputStream, false);

        if (session instanceof Session) {
          ((Session) session).allocateResultLob(resultLob, inputStream);
        } else {
          currentResult.addLobResult(resultLob);
        }

        hasLob = true;

        continue;
      } else if (addedResultMode == ResultConstants.NONE) {
        break;
      } else {
        throw Error.runtimeError(ErrorCode.U_S0500, "Result");
      }
    }

    if (hasLob) {
      ((Session) session).registerResultLobs(currentResult);
    }
  }

  private static Result newResult(Session session, DataInput dataInput, RowInputBinary in, int mode)
      throws IOException, HsqlException {

    Result result = newResult(mode);
    int length = dataInput.readInt();

    in.resetRow(0, length);

    byte[] byteArray = in.getBuffer();
    final int offset = 4;

    dataInput.readFully(byteArray, offset, length - offset);

    switch (mode) {
      case ResultConstants.GETSESSIONATTR:
        result.statementReturnType = in.readByte();
        break;

      case ResultConstants.DISCONNECT:
      case ResultConstants.RESETSESSION:
      case ResultConstants.STARTTRAN:
        break;

      case ResultConstants.PREPARE:
        result.setStatementType(in.readByte());

        result.mainString = in.readString();
        result.rsProperties = in.readByte();
        result.generateKeys = in.readByte();

        if (result.generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_NAMES
            || result.generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_INDEXES) {
          result.generatedMetaData = new ResultMetaData(in);
        }
        break;

      case ResultConstants.CLOSE_RESULT:
        result.id = in.readLong();
        break;

      case ResultConstants.FREESTMT:
        result.statementID = in.readLong();
        break;

      case ResultConstants.EXECDIRECT:
        result.updateCount = in.readInt();
        result.fetchSize = in.readInt();
        result.statementReturnType = in.readByte();
        result.mainString = in.readString();
        result.rsProperties = in.readByte();
        result.queryTimeout = in.readShort();
        result.generateKeys = in.readByte();

        if (result.generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_NAMES
            || result.generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_INDEXES) {
          result.generatedMetaData = new ResultMetaData(in);
        }
        break;

      case ResultConstants.CONNECT:
        result.databaseName = in.readString();
        result.mainString = in.readString();
        result.subString = in.readString();
        result.zoneString = in.readString();
        result.updateCount = in.readInt();
        break;

      case ResultConstants.ERROR:
      case ResultConstants.WARNING:
        result.mainString = in.readString();
        result.subString = in.readString();
        result.errorCode = in.readInt();
        break;

      case ResultConstants.CONNECTACKNOWLEDGE:
        result.databaseID = in.readInt();
        result.sessionID = in.readLong();
        result.databaseName = in.readString();
        result.mainString = in.readString();
        break;

      case ResultConstants.UPDATECOUNT:
        result.updateCount = in.readInt();
        break;

      case ResultConstants.ENDTRAN:
        {
          int type = in.readInt();

          result.setActionType(type); // endtran type

          switch (type) {
            case ResultConstants.TX_SAVEPOINT_NAME_RELEASE:
            case ResultConstants.TX_SAVEPOINT_NAME_ROLLBACK:
              result.mainString = in.readString(); // savepoint name
              break;

            case ResultConstants.TX_COMMIT:
            case ResultConstants.TX_ROLLBACK:
            case ResultConstants.TX_COMMIT_AND_CHAIN:
            case ResultConstants.TX_ROLLBACK_AND_CHAIN:
            case ResultConstants.PREPARECOMMIT:
              break;

            default:
              throw Error.runtimeError(ErrorCode.U_S0500, "Result");
          }

          break;
        }
      case ResultConstants.SETCONNECTATTR:
        {
          int type = in.readInt(); // attr type

          result.setConnectionAttrType(type);

          switch (type) {
            case ResultConstants.SQL_ATTR_SAVEPOINT_NAME:
              result.mainString = in.readString(); // savepoint name
              break;

              //  case ResultConstants.SQL_ATTR_AUTO_IPD :
              //      - always true
              //  default: throw - case never happens
            default:
              throw Error.runtimeError(ErrorCode.U_S0500, "Result");
          }

          break;
        }
      case ResultConstants.PREPARE_ACK:
        result.statementReturnType = in.readByte();
        result.statementID = in.readLong();
        result.rsProperties = in.readByte();
        result.metaData = new ResultMetaData(in);
        result.parameterMetaData = new ResultMetaData(in);
        break;

      case ResultConstants.CALL_RESPONSE:
        result.updateCount = in.readInt();
        result.fetchSize = in.readInt();
        result.statementID = in.readLong();
        result.statementReturnType = in.readByte();
        result.rsProperties = in.readByte();
        result.metaData = new ResultMetaData(in);
        result.valueData = readSimple(in, result.metaData);
        break;

      case ResultConstants.EXECUTE:
        result.updateCount = in.readInt();
        result.fetchSize = in.readInt();
        result.statementID = in.readLong();
        result.rsProperties = in.readByte();
        result.queryTimeout = in.readShort();

        Statement statement = session.statementManager.getStatement(session, result.statementID);

        if (statement == null) {

          // invalid statement
          result.mode = ResultConstants.EXECUTE_INVALID;
          result.valueData = ValuePool.emptyObjectArray;

          break;
        }

        result.statement = statement;
        result.metaData = result.statement.getParametersMetaData();
        result.valueData = readSimple(in, result.metaData);
        break;

      case ResultConstants.UPDATE_RESULT:
        {
          result.id = in.readLong();

          int type = in.readInt();

          result.setActionType(type);

          result.metaData = new ResultMetaData(in);
          result.valueData = readSimple(in, result.metaData);

          break;
        }
      case ResultConstants.BATCHEXECRESPONSE:
      case ResultConstants.BATCHEXECUTE:
      case ResultConstants.BATCHEXECDIRECT:
      case ResultConstants.SETSESSIONATTR:
        {
          result.updateCount = in.readInt();
          result.fetchSize = in.readInt();
          result.statementID = in.readLong();
          result.queryTimeout = in.readShort();
          result.metaData = new ResultMetaData(in);

          result.navigator.readSimple(in, result.metaData);

          break;
        }
      case ResultConstants.PARAM_METADATA:
        {
          result.metaData = new ResultMetaData(in);

          result.navigator.read(in, result.metaData);

          break;
        }
      case ResultConstants.REQUESTDATA:
        {
          result.id = in.readLong();
          result.updateCount = in.readInt();
          result.fetchSize = in.readInt();

          break;
        }
      case ResultConstants.DATAHEAD:
      case ResultConstants.DATA:
      case ResultConstants.GENERATED:
        {
          result.id = in.readLong();
          result.updateCount = in.readInt();
          result.fetchSize = in.readInt();
          result.rsProperties = in.readByte();
          result.metaData = new ResultMetaData(in);
          result.navigator = new RowSetNavigatorClient();

          result.navigator.read(in, result.metaData);

          break;
        }
      case ResultConstants.DATAROWS:
        {
          result.metaData = new ResultMetaData(in);
          result.navigator = new RowSetNavigatorClient();

          result.navigator.read(in, result.metaData);

          break;
        }
      default:
        throw Error.runtimeError(ErrorCode.U_S0500, "Result");
    }

    return result;
  }

  /** For interval PSM return values */
  public static Result newPSMResult(int type, String label, Object value) {

    Result result = newResult(ResultConstants.VALUE);

    result.errorCode = type;
    result.mainString = label;
    result.valueData = value;

    return result;
  }

  /** For interval PSM return values */
  public static Result newPSMResult(Object value) {

    Result result = newResult(ResultConstants.VALUE);

    result.valueData = value;

    return result;
  }

  /** For SQLPREPARE For parparation of SQL parepared statements. */
  public static Result newPrepareStatementRequest() {
    return newResult(ResultConstants.PREPARE);
  }

  /**
   * For SQLEXECUTE For execution of SQL prepared statements. The parameters are set afterwards as
   * the Result is reused
   */
  public static Result newPreparedExecuteRequest(Type[] types, long statementId) {

    Result result = newResult(ResultConstants.EXECUTE);

    result.metaData = ResultMetaData.newSimpleResultMetaData(types);
    result.statementID = statementId;
    result.valueData = ValuePool.emptyObjectArray;

    return result;
  }

  /** For CALL_RESPONSE For execution of SQL callable statements. */
  public static Result newCallResponse(Type[] types, long statementId, Object[] values) {

    Result result = newResult(ResultConstants.CALL_RESPONSE);

    result.metaData = ResultMetaData.newSimpleResultMetaData(types);
    result.statementID = statementId;
    result.valueData = values;

    return result;
  }

  /** For UPDATE_RESULT The parameters are set afterwards as the Result is reused */
  public static Result newUpdateResultRequest(Type[] types, long id) {

    Result result = newResult(ResultConstants.UPDATE_RESULT);

    result.metaData = ResultMetaData.newUpdateResultMetaData(types);
    result.id = id;
    result.valueData = new Object[] {};

    return result;
  }

  /** For UPDATE_RESULT results The parameters are set by this method as the Result is reused */
  public void setPreparedResultUpdateProperties(Object[] parameterValues) {
    valueData = parameterValues;
  }

  /** For SQLEXECUTE results The parameters are set by this method as the Result is reused */
  public void setPreparedExecuteProperties(
      Object[] parameterValues, int maxRows, int fetchSize, int resultProps, int timeout) {

    mode = ResultConstants.EXECUTE;
    valueData = parameterValues;
    updateCount = maxRows;
    this.fetchSize = fetchSize;
    this.rsProperties = resultProps;
    queryTimeout = timeout;
  }

  /** For BATCHEXECUTE */
  public void setBatchedPreparedExecuteRequest() {

    mode = ResultConstants.BATCHEXECUTE;

    if (navigator == null) {
      navigator = new RowSetNavigatorClient(4);
    } else {
      navigator.clear();
    }

    updateCount = 0;
    this.fetchSize = 0;
  }

  public void addBatchedPreparedExecuteRequest(Object[] parameterValues) {
    ((RowSetNavigatorClient) navigator).add(parameterValues);
  }

  /** For BATCHEXECDIRECT */
  public static Result newBatchedExecuteRequest() {

    Type[] types = new Type[] {Type.SQL_VARCHAR};
    Result result = newResult(ResultConstants.BATCHEXECDIRECT);

    result.metaData = ResultMetaData.newSimpleResultMetaData(types);

    return result;
  }

  /** For BATCHEXERESPONSE for a BATCHEXECUTE or BATCHEXECDIRECT */
  public static Result newBatchedExecuteResponse(
      int[] updateCounts, Result generatedResult, Result e) {

    Result result = newResult(ResultConstants.BATCHEXECRESPONSE);

    result.addChainedResult(generatedResult);
    result.addChainedResult(e);

    Type[] types = new Type[] {Type.SQL_INTEGER};

    result.metaData = ResultMetaData.newSimpleResultMetaData(types);

    Object[][] table = new Object[updateCounts.length][];

    for (int i = 0; i < updateCounts.length; i++) {
      table[i] = new Object[] {ValuePool.getInt(updateCounts[i])};
    }

    ((RowSetNavigatorClient) result.navigator).setData(table);

    return result;
  }

  public static Result newResetSessionRequest() {

    Result result = newResult(ResultConstants.RESETSESSION);

    return result;
  }

  public static Result newConnectionAttemptRequest(
      String user, String password, String database, String zoneString, int timeZoneSeconds) {

    Result result = newResult(ResultConstants.CONNECT);

    result.mainString = user;
    result.subString = password;
    result.zoneString = zoneString;
    result.databaseName = database;
    result.updateCount = timeZoneSeconds;

    return result;
  }

  public static Result newConnectionAcknowledgeResponse(
      Database database, long sessionID, int databaseID) {

    Result result = newResult(ResultConstants.CONNECTACKNOWLEDGE);

    result.sessionID = sessionID;
    result.databaseID = databaseID;
    result.databaseName = database.getUniqueName();
    result.mainString = database.getProperties().getClientPropertiesAsString();

    return result;
  }

  public static Result newUpdateZeroResult() {
    return new Result(ResultConstants.UPDATECOUNT, 0);
  }

  public static Result newUpdateCountResult(int count) {
    return new Result(ResultConstants.UPDATECOUNT, count);
  }

  public static Result newUpdateCountResult(ResultMetaData meta, int count) {

    Result result = newResult(ResultConstants.UPDATECOUNT);
    Result dataResult = newGeneratedDataResult(meta);

    result.updateCount = count;

    result.addChainedResult(dataResult);

    return result;
  }

  public static Result newSingleColumnResult(ResultMetaData meta) {

    Result result = newResult(ResultConstants.DATA);

    result.metaData = meta;
    result.navigator = new RowSetNavigatorClient();

    return result;
  }

  public static Result newSingleColumnResult(String colName) {

    Result result = newResult(ResultConstants.DATA);

    result.metaData = ResultMetaData.newSingleColumnMetaData(colName);
    result.navigator = new RowSetNavigatorClient(8);

    return result;
  }

  public static Result newSingleColumnStringResult(String colName, String contents) {

    Result result = Result.newSingleColumnResult(colName);
    LineNumberReader lnr = new LineNumberReader(new StringReader(contents));

    while (true) {
      String line = null;

      try {
        line = lnr.readLine();
      } catch (Exception e) {
      }

      if (line == null) {
        break;
      }

      result.getNavigator().add(new Object[] {line});
    }

    return result;
  }

  public static Result newPrepareResponse(Statement statement) {

    Result r = newResult(ResultConstants.PREPARE_ACK);

    r.statement = statement;
    r.statementID = statement.getID();

    int csType = statement.getType();

    r.statementReturnType = statement.getStatementReturnType();
    r.metaData = statement.getResultMetaData();
    r.parameterMetaData = statement.getParametersMetaData();

    return r;
  }

  public static Result newFreeStmtRequest(long statementID) {

    Result r = newResult(ResultConstants.FREESTMT);

    r.statementID = statementID;

    return r;
  }

  /**
   * For direct execution of SQL statements. The statement and other parameters are set afterwards
   * as the Result is reused
   */
  public static Result newExecuteDirectRequest() {
    return newResult(ResultConstants.EXECDIRECT);
  }

  /** For both EXECDIRECT and PREPARE */
  public void setPrepareOrExecuteProperties(
      String sql,
      int maxRows,
      int fetchSize,
      int statementReturnType,
      int timeout,
      int resultSetProperties,
      int keyMode,
      int[] generatedIndexes,
      String[] generatedNames) {

    mainString = sql;
    updateCount = maxRows;
    this.fetchSize = fetchSize;
    this.statementReturnType = statementReturnType;
    queryTimeout = timeout;
    rsProperties = resultSetProperties;
    generateKeys = keyMode;
    generatedMetaData =
        ResultMetaData.newGeneratedColumnsMetaData(generatedIndexes, generatedNames);
  }

  public static Result newSetSavepointRequest(String name) {

    Result result;

    result = newResult(ResultConstants.SETCONNECTATTR);

    result.setConnectionAttrType(ResultConstants.SQL_ATTR_SAVEPOINT_NAME);
    result.setMainString(name);

    return result;
  }

  public static Result newRequestDataResult(long id, int offset, int count) {

    Result result = newResult(ResultConstants.REQUESTDATA);

    result.id = id;
    result.updateCount = offset;
    result.fetchSize = count;

    return result;
  }

  public static Result newDataResult(ResultMetaData md) {

    Result result = newResult(ResultConstants.DATA);

    result.navigator = new RowSetNavigatorClient();
    result.metaData = md;

    return result;
  }

  public static Result newGeneratedDataResult(ResultMetaData md) {

    Result result = newResult(ResultConstants.GENERATED);

    result.navigator = new RowSetNavigatorClient();
    result.metaData = md;

    return result;
  }

  /** initially, only used for updatability */
  public int getExecuteProperties() {
    return rsProperties;
  }

  public static Result newDataHeadResult(
      SessionInterface session, Result source, int offset, int count) {

    if (offset + count > source.navigator.getSize()) {
      count = source.navigator.getSize() - offset;
    }

    Result result = newResult(ResultConstants.DATAHEAD);

    result.metaData = source.metaData;
    result.navigator = new RowSetNavigatorClient(source.navigator, offset, count);

    result.navigator.setId(source.navigator.getId());
    result.setSession(session);

    result.rsProperties = source.rsProperties;
    result.fetchSize = source.fetchSize;

    return result;
  }

  public static Result newDataRowsResult(Result source, int offset, int count) {

    if (offset + count > source.navigator.getSize()) {
      count = source.navigator.getSize() - offset;
    }

    Result result = newResult(ResultConstants.DATAROWS);

    result.id = source.id;
    result.metaData = source.metaData;
    result.navigator = new RowSetNavigatorClient(source.navigator, offset, count);

    return result;
  }

  public static Result newDataRowsResult(RowSetNavigator navigator) {

    Result result = newResult(ResultConstants.DATAROWS);

    result.navigator = navigator;

    return result;
  }

  /** Result structure used for set/get session attributes */
  public static Result newSessionAttributesResult() {

    Result result = newResult(ResultConstants.DATA);

    result.navigator = new RowSetNavigatorClient(1);
    result.metaData = sessionAttributesMetaData;

    result.navigator.add(new Object[SessionInterface.INFO_LIMIT]);

    return result;
  }

  public static Result newWarningResult(HsqlException w) {

    Result result = newResult(ResultConstants.WARNING);

    result.mainString = w.getMessage();
    result.subString = w.getSQLState();
    result.errorCode = w.getErrorCode();

    return result;
  }

  public static Result newErrorResult(Throwable t) {
    return newErrorResult(t, null);
  }

  /** @todo 1.9.0 fredt - move the messages to Error.java */
  public static Result newErrorResult(Throwable t, String statement) {

    Result result = newResult(ResultConstants.ERROR);

    if (t instanceof HsqlException) {
      result.exception = (HsqlException) t;
      result.mainString = result.exception.getMessage();
      result.subString = result.exception.getSQLState();

      if (statement != null) {
        result.mainString += " in statement [" + statement + "]";
      }

      result.errorCode = result.exception.getErrorCode();
    } else if (t instanceof OutOfMemoryError) {

      // gc() at this point may clear the memory allocated so far

      /** @todo 1.9.0 - review if it's better to gc higher up the stack */
      System.gc();

      result.exception = Error.error(ErrorCode.OUT_OF_MEMORY, t);
      result.mainString = result.exception.getMessage();
      result.subString = result.exception.getSQLState();
      result.errorCode = result.exception.getErrorCode();
    } else {
      result.exception = Error.error(ErrorCode.GENERAL_ERROR, t);
      result.mainString = result.exception.getMessage() + " " + t.toString();
      result.subString = result.exception.getSQLState();
      result.errorCode = result.exception.getErrorCode();

      if (statement != null) {
        result.mainString += " in statement [" + statement + "]";
      }
    }

    return result;
  }

  public void write(SessionInterface session, DataOutputStream dataOut, RowOutputInterface rowOut)
      throws IOException, HsqlException {

    rowOut.reset();
    rowOut.writeByte(mode);

    int startPos = rowOut.size();

    rowOut.writeSize(0);

    switch (mode) {
      case ResultConstants.GETSESSIONATTR:
        rowOut.writeByte(statementReturnType);
        break;

      case ResultConstants.DISCONNECT:
      case ResultConstants.RESETSESSION:
      case ResultConstants.STARTTRAN:
        break;

      case ResultConstants.PREPARE:
        rowOut.writeByte(statementReturnType);
        rowOut.writeString(mainString);
        rowOut.writeByte(rsProperties);
        rowOut.writeByte(generateKeys);

        if (generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_NAMES
            || generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_INDEXES) {
          generatedMetaData.write(rowOut);
        }
        break;

      case ResultConstants.FREESTMT:
        rowOut.writeLong(statementID);
        break;

      case ResultConstants.CLOSE_RESULT:
        rowOut.writeLong(id);
        break;

      case ResultConstants.EXECDIRECT:
        rowOut.writeInt(updateCount);
        rowOut.writeInt(fetchSize);
        rowOut.writeByte(statementReturnType);
        rowOut.writeString(mainString);
        rowOut.writeByte(rsProperties);
        rowOut.writeShort(queryTimeout);
        rowOut.writeByte(generateKeys);

        if (generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_NAMES
            || generateKeys == ResultConstants.RETURN_GENERATED_KEYS_COL_INDEXES) {
          generatedMetaData.write(rowOut);
        }
        break;

      case ResultConstants.CONNECT:
        rowOut.writeString(databaseName);
        rowOut.writeString(mainString);
        rowOut.writeString(subString);
        rowOut.writeString(zoneString);
        rowOut.writeInt(updateCount);
        break;

      case ResultConstants.ERROR:
      case ResultConstants.WARNING:
        rowOut.writeString(mainString);
        rowOut.writeString(subString);
        rowOut.writeInt(errorCode);
        break;

      case ResultConstants.CONNECTACKNOWLEDGE:
        rowOut.writeInt(databaseID);
        rowOut.writeLong(sessionID);
        rowOut.writeString(databaseName);
        rowOut.writeString(mainString);
        break;

      case ResultConstants.UPDATECOUNT:
        rowOut.writeInt(updateCount);
        break;

      case ResultConstants.ENDTRAN:
        {
          int type = getActionType();

          rowOut.writeInt(type); // endtran type

          switch (type) {
            case ResultConstants.TX_SAVEPOINT_NAME_RELEASE:
            case ResultConstants.TX_SAVEPOINT_NAME_ROLLBACK:
              rowOut.writeString(mainString); // savepoint name
              break;

            case ResultConstants.TX_COMMIT:
            case ResultConstants.TX_ROLLBACK:
            case ResultConstants.TX_COMMIT_AND_CHAIN:
            case ResultConstants.TX_ROLLBACK_AND_CHAIN:
            case ResultConstants.PREPARECOMMIT:
              break;

            default:
              throw Error.runtimeError(ErrorCode.U_S0500, "Result");
          }

          break;
        }
      case ResultConstants.PREPARE_ACK:
        rowOut.writeByte(statementReturnType);
        rowOut.writeLong(statementID);
        rowOut.writeByte(rsProperties);
        metaData.write(rowOut);
        parameterMetaData.write(rowOut);
        break;

      case ResultConstants.CALL_RESPONSE:
        rowOut.writeInt(updateCount);
        rowOut.writeInt(fetchSize);
        rowOut.writeLong(statementID);
        rowOut.writeByte(statementReturnType);
        rowOut.writeByte(rsProperties);
        metaData.write(rowOut);
        writeSimple(rowOut, metaData, (Object[]) valueData);
        break;

      case ResultConstants.EXECUTE:
        rowOut.writeInt(updateCount);
        rowOut.writeInt(fetchSize);
        rowOut.writeLong(statementID);
        rowOut.writeByte(rsProperties);
        rowOut.writeShort(queryTimeout);
        writeSimple(rowOut, metaData, (Object[]) valueData);
        break;

      case ResultConstants.UPDATE_RESULT:
        rowOut.writeLong(id);
        rowOut.writeInt(getActionType());
        metaData.write(rowOut);
        writeSimple(rowOut, metaData, (Object[]) valueData);
        break;

      case ResultConstants.BATCHEXECRESPONSE:
      case ResultConstants.BATCHEXECUTE:
      case ResultConstants.BATCHEXECDIRECT:
      case ResultConstants.SETSESSIONATTR:
        {
          rowOut.writeInt(updateCount);
          rowOut.writeInt(fetchSize);
          rowOut.writeLong(statementID);
          rowOut.writeShort(queryTimeout);
          metaData.write(rowOut);
          navigator.writeSimple(rowOut, metaData);

          break;
        }
      case ResultConstants.PARAM_METADATA:
        {
          metaData.write(rowOut);
          navigator.write(rowOut, metaData);

          break;
        }
      case ResultConstants.SETCONNECTATTR:
        {
          int type = getConnectionAttrType();

          rowOut.writeInt(type); // attr type / updateCount

          switch (type) {
            case ResultConstants.SQL_ATTR_SAVEPOINT_NAME:
              rowOut.writeString(mainString); // savepoint name
              break;

              // case ResultConstants.SQL_ATTR_AUTO_IPD // always true
              // default: // throw, but case never happens
            default:
              throw Error.runtimeError(ErrorCode.U_S0500, "Result");
          }

          break;
        }
      case ResultConstants.REQUESTDATA:
        {
          rowOut.writeLong(id);
          rowOut.writeInt(updateCount);
          rowOut.writeInt(fetchSize);

          break;
        }
      case ResultConstants.DATAROWS:
        metaData.write(rowOut);
        navigator.write(rowOut, metaData);
        break;

      case ResultConstants.DATAHEAD:
      case ResultConstants.DATA:
      case ResultConstants.GENERATED:
        rowOut.writeLong(id);
        rowOut.writeInt(updateCount);
        rowOut.writeInt(fetchSize);
        rowOut.writeByte(rsProperties);
        metaData.write(rowOut);
        navigator.write(rowOut, metaData);
        break;

      default:
        throw Error.runtimeError(ErrorCode.U_S0500, "Result");
    }

    rowOut.writeIntData(rowOut.size() - startPos, startPos);
    dataOut.write(rowOut.getOutputStream().getBuffer(), 0, rowOut.size());

    int count = getLobCount();
    Result current = this;

    for (int i = 0; i < count; i++) {
      ResultLob lob = current.lobResults;

      lob.writeBody(session, dataOut);

      current = current.lobResults;
    }

    if (chainedResult == null) {
      dataOut.writeByte(ResultConstants.NONE);
    } else {
      chainedResult.write(session, dataOut, rowOut);
    }

    dataOut.flush();
  }

  public int getType() {
    return mode;
  }

  public boolean isData() {
    return mode == ResultConstants.DATA || mode == ResultConstants.DATAHEAD;
  }

  public boolean isError() {
    return mode == ResultConstants.ERROR;
  }

  public boolean isWarning() {
    return mode == ResultConstants.WARNING;
  }

  public boolean isUpdateCount() {
    return mode == ResultConstants.UPDATECOUNT;
  }

  public boolean isSimpleValue() {
    return mode == ResultConstants.VALUE;
  }

  public boolean hasGeneratedKeys() {
    return mode == ResultConstants.UPDATECOUNT && chainedResult != null;
  }

  public HsqlException getException() {
    return exception;
  }

  public long getStatementID() {
    return statementID;
  }

  public void setStatementID(long statementId) {
    this.statementID = statementId;
  }

  public String getMainString() {
    return mainString;
  }

  public void setMainString(String sql) {
    this.mainString = sql;
  }

  public String getSubString() {
    return subString;
  }

  public String getZoneString() {
    return zoneString;
  }

  public int getErrorCode() {
    return errorCode;
  }

  public Object getValueObject() {
    return valueData;
  }

  public void setValueObject(Object value) {
    valueData = value;
  }

  public Statement getStatement() {
    return statement;
  }

  public void setStatement(Statement statement) {
    this.statement = statement;
  }

  public String getDatabaseName() {
    return databaseName;
  }

  public void setMaxRows(int count) {
    updateCount = count;
  }

  public int getFetchSize() {
    return this.fetchSize;
  }

  public void setFetchSize(int count) {
    fetchSize = count;
  }

  public int getUpdateCount() {
    return updateCount;
  }

  public int getConnectionAttrType() {
    return updateCount;
  }

  public void setConnectionAttrType(int type) {
    updateCount = type;
  }

  public int getActionType() {
    return updateCount;
  }

  public void setActionType(int type) {
    updateCount = type;
  }

  public long getSessionId() {
    return sessionID;
  }

  public void setSessionId(long id) {
    sessionID = id;
  }

  public void setSession(SessionInterface session) {

    if (navigator != null) {
      navigator.setSession(session);
    }
  }

  public int getDatabaseId() {
    return databaseID;
  }

  public void setDatabaseId(int id) {
    databaseID = id;
  }

  public long getResultId() {
    return id;
  }

  public void setResultId(long id) {

    this.id = id;

    if (navigator != null) {
      navigator.setId(id);
    }
  }

  public void setUpdateCount(int count) {
    updateCount = count;
  }

  public void setAsTransactionEndRequest(int subType, String savepoint) {

    mode = ResultConstants.ENDTRAN;
    updateCount = subType;
    mainString = savepoint == null ? "" : savepoint;
  }

  public Object[] getSingleRowData() {

    Object[] data = (Object[]) initialiseNavigator().getNext();

    data = (Object[]) ArrayUtil.resizeArrayIfDifferent(data, metaData.getColumnCount());

    return data;
  }

  public Object[] getParameterData() {
    return (Object[]) valueData;
  }

  public Object[] getSessionAttributes() {
    return (Object[]) initialiseNavigator().getNext();
  }

  public void setResultType(int type) {
    mode = (byte) type;
  }

  public void setStatementType(int type) {
    statementReturnType = type;
  }

  public int getStatementType() {
    return statementReturnType;
  }

  public int getGeneratedResultType() {
    return generateKeys;
  }

  public ResultMetaData getGeneratedResultMetaData() {
    return generatedMetaData;
  }

  public Result getChainedResult() {
    return chainedResult;
  }

  public Result getUnlinkChainedResult() {

    Result result = chainedResult;

    chainedResult = null;

    return result;
  }

  public void addChainedResult(Result result) {

    Result current = this;

    while (current.chainedResult != null) {
      current = current.chainedResult;
    }

    current.chainedResult = result;
  }

  public void addWarnings(HsqlException[] warnings) {

    for (int i = 0; i < warnings.length; i++) {
      Result warning = newWarningResult(warnings[i]);

      addChainedResult(warning);
    }
  }

  public int getLobCount() {
    return lobCount;
  }

  public ResultLob getLOBResult() {
    return lobResults;
  }

  public void addLobResult(ResultLob result) {

    Result current = this;

    while (current.lobResults != null) {
      current = current.lobResults;
    }

    current.lobResults = result;

    lobCount++;
  }

  public void clearLobResults() {
    lobResults = null;
    lobCount = 0;
  }

  private static Object[] readSimple(RowInputBinary in, ResultMetaData meta) throws IOException {

    int size = in.readInt();

    return in.readData(meta.columnTypes);
  }

  private static void writeSimple(RowOutputInterface out, ResultMetaData meta, Object[] data)
      throws IOException {

    out.writeInt(1);
    out.writeData(meta.getColumnCount(), meta.columnTypes, data, null, null);
  }

  // ----------- Navigation
  public RowSetNavigator navigator;

  public RowSetNavigator getNavigator() {
    return navigator;
  }

  public void setNavigator(RowSetNavigator navigator) {
    this.navigator = navigator;
  }

  public RowSetNavigator initialiseNavigator() {

    switch (mode) {
      case ResultConstants.BATCHEXECUTE:
      case ResultConstants.BATCHEXECDIRECT:
      case ResultConstants.BATCHEXECRESPONSE:
      case ResultConstants.SETSESSIONATTR:
      case ResultConstants.PARAM_METADATA:
        navigator.beforeFirst();

        return navigator;

      case ResultConstants.DATA:
      case ResultConstants.DATAHEAD:
      case ResultConstants.GENERATED:
        navigator.reset();

        return navigator;

      default:
        throw Error.runtimeError(ErrorCode.U_S0500, "Result");
    }
  }
}