N
N
Nikolay Stupak2015-06-09 14:47:09
Java
Nikolay Stupak, 2015-06-09 14:47:09

Why does reading a blob from hsqldb throw a java.lang.IndexOutOfBoundsException?

The following code is used to write and read blobs from the database:

public class LargeObjectHandler implements TypeHandler<LargeObject> {
  private static final long MAX_ALLOWED_SIZE = 1024 * 150 * 10;//150 kiB
  
  @Override
  public LargeObject read(TypeHandlerContext context, ResultSet resultSet) {
    try {
      Blob blob = resultSet.getBlob(context.getColumn());
      if(blob == null) {
        return null;
      }
      final long len = blob.length();
      if(len <= MAX_ALLOWED_SIZE) {//читаем сразу массив
        return new ByteArrayLargeObject(readBytes((int)len, blob), true, false);
      } else {
        return new NonAttachedLargeObject(len);
      }
    } catch(Exception e) {
      Throwables.throwAsUndeclarable(e);
      return null;
    }
  }

  private byte[] readBytes(int len, Blob blob) throws Exception {
    try {
      return blob.getBytes(1l, len);
    } catch(SQLFeatureNotSupportedException e) {
      InputStream is = blob.getBinaryStream();
      try {
        return IOUtils.toByteArray(is, len);
      } finally {
        Tools.closeSilent(is);
      }
    }
  }

  @Override
  public void write(TypeHandlerContext context, ResultSet resultSet, Object value) {
    int column = context.getColumn();
    LargeObject lo = (LargeObject)value;
    if(lo == null) {
      try {
        resultSet.updateNull(column);
      } catch(SQLException e) {
        throw new RuntimeException(e);
      }
    } else {
      InputStream is = lo.getInputStream();
      try {
        if(is == null) {
          Reader r = lo.getReader();
          if(r == null) {
            resultSet.updateNull(column);
            return;//empty Large object
          }
          is = new ReaderInputStream(r, "UTF-8");
        } 
        resultSet.updateBlob(column, is, lo.getLength());
      } catch(Exception e) {
        Throwables.throwAsUndeclarable(e);
      } finally {
        Tools.closeSilent(is);
      }
    }
  }

}

For testing, an InputSteam is used containing the following text:
and(
 gt(
  field("sinSystems.childsCount"),
  param(0, "integer")
 ),
 security.checkAccessForTarget("sinSystems.target", "sinSystems.targetType")
)

Everything works fine on oracle and postresql, but when reading a blob on hsqldb, the following error occurs:
Caused by: java.lang.IndexOutOfBoundsException: Index out of bounds: 0 >= 0
        at org.hsqldb.lib.HsqlArrayList.get(Unknown Source)
        at org.hsqldb.persist.LobStoreMem.getBlockBytes(Unknown Source)
        at org.hsqldb.persist.LobManager.getBytesNormal(Unknown Source)
        at org.hsqldb.persist.LobManager.getBytes(Unknown Source)
        at org.hsqldb.Session.performLOBOperation(Unknown Source)
        at org.hsqldb.Session.execute(Unknown Source)
        at org.hsqldb.types.BlobDataID.getBytes(Unknown Source)
        at org.hsqldb.jdbc.JDBCBlobClient.getBytes(Unknown Source)
        at ru.kih.sin.db.types.LargeObjectHandler.readBytes(LargeObjectHandler.java:100)
        at ru.kih.sin.db.types.LargeObjectHandler.read(LargeObjectHandler.java:44)
        at ru.kih.sin.db.types.LargeObjectHandler.read(LargeObjectHandler.java:27)
        at ru.kih.sql.types.NullValueWrapper.read(NullValueWrapper.java:24)
        at ru.kih.sql.types.TypeHandlerWrapper.read(TypeHandlerWrapper.java:74)
        at ru.kih.sql.types.TypeConverter.read(TypeConverter.java:48)
        at ru.kih.sin.model.impl.ObjectReadFunction.apply(ObjectReadFunction.java:119)
        at ru.kih.sin.model.impl.ObjectHandler.handle(ObjectHandler.java:41)
        at ru.kih.sin.model.impl.ObjectHandler.handle(ObjectHandler.java:16)
        at ru.kih.sql.query.handlers.SimpleSelect.handle(SimpleSelect.java:63)
        at ru.kih.sql.query.QueryHelper$UnsafeFunctionImpl.apply(QueryHelper.java:39)
        at ru.kih.sql.query.QueryHelper$UnsafeFunctionImpl.apply(QueryHelper.java:25)
        at ru.kih.sql.query.QueryContext.runIn(QueryContext.java:479)
        at ru.kih.sql.query.QueryContext.run(QueryContext.java:228)
        at ru.kih.sql.query.QueryContext.run(QueryContext.java:212)
        at ru.kih.sql.query.QueryContext.call(QueryContext.java:242)
        at ru.kih.sql.query.QueryHelper.query(QueryHelper.java:57)
        at ru.kih.sin.model.impl.SelectLobFunction.apply(SelectLobFunction.java:62)
        at ru.kih.sin.model.impl.SelectLobFunction.apply(SelectLobFunction.java:24)
        at ru.kih.sql.query.QueryContext.runIn(QueryContext.java:479)
        at ru.kih.sql.query.QueryContext.run(QueryContext.java:228)
        at ru.kih.sql.query.QueryContext.run(QueryContext.java:212)
        at ru.kih.sql.query.QueryContext.call(QueryContext.java:242)
        at ru.kih.sin.model.impl.ModelImpl.lambda$loadLob$8(ModelImpl.java:280)
        at ru.kih.sin.model.impl.ModelImpl$$Lambda$16/26663026.call(Unknown Source)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at ru.kih.sin.model.impl.ModelImpl.loadLob(ModelImpl.java:285)
        ... 48 more

The 100th line in LargeObjectHandler.java is the line in the methodreturn blob.getBytes(1l, len);
private byte[] readBytes(int len, Blob blob) throws Exception

In the debugger, you can see that blob.getLength() returns a number greater than 0. Also, if you execute
InputStream is = blob.getBinaryStream();
is.available()

then the result is the same number as in blob.getLength() (greater than 0), but calling any of the is.read() methods also throws this error.
The hsqldb-2.3.1 driver is used to connect to the database.
Tell me please, what could be the problem?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
N
Nikolai Stupak, 2015-06-10
@nikolaas

The question is closed. The problem was in the Tools.closeSilent(is);method line

public void write(TypeHandlerContext context, ResultSet resultSet, Object value)
, which closed InputStreambefore the call to resultSet.updateRow. Apparently on oracle and postgresql when called
resultSet.updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException
data is copied from the passed stream, and a link to it is stored in hsqldb.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question