I get an unexpected IndexOutOfBounds-Exception in the LobStorageBackend 
when reading blobs from a table which gets concurrently updated from 
another thread (however the update is not performed on the blob-field).

I attached a simple test case for that and I'm able to reproduce that quite 
reliable on MacOS and Android. Pastebin: http://pastebin.com/3N99rZNp

The stack trace is:

Exception in thread "main" java.util.concurrent.ExecutionException: 
org.h2.jdbc.JdbcSQLException: Eingabe/Ausgabe: "java.io.IOException: 
java.lang.ArrayIndexOutOfBoundsException: 0"; "lob: null table: -1 id: 1950"
IO Exception: "java.io.IOException: java.lang.ArrayIndexOutOfBoundsException: 
0"; "lob: null table: -1 id: 1950" [90031-176]
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
at java.util.concurrent.FutureTask.get(FutureTask.java:83)
at org.h2.samples.HelloWorld.main(HelloWorld.java:83)
Caused by: org.h2.jdbc.JdbcSQLException: Eingabe/Ausgabe: "
java.io.IOException: java.lang.ArrayIndexOutOfBoundsException: 0"; "lob: 
null table: -1 id: 1950"
IO Exception: "java.io.IOException: java.lang.ArrayIndexOutOfBoundsException: 
0"; "lob: null table: -1 id: 1950" [90031-176]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:344)
at org.h2.message.DbException.get(DbException.java:167)
at org.h2.message.DbException.convertIOException(DbException.java:329)
at org.h2.value.ValueLobDb.getBytesNoCopy(ValueLobDb.java:329)
at org.h2.value.ValueLobDb.getBytes(ValueLobDb.java:313)
at org.h2.jdbc.JdbcResultSet.getBytes(JdbcResultSet.java:1056)
at org.h2.samples.HelloWorld$1.call(HelloWorld.java:58)
at org.h2.samples.HelloWorld$1.call(HelloWorld.java:1)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(
ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(
ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:695)

Caused by: java.io.IOException: java.lang.ArrayIndexOutOfBoundsException: 0
at org.h2.message.DbException.convertToIOException(DbException.java:363)
at org.h2.util.IOUtils.copy(IOUtils.java:181)
at org.h2.util.IOUtils.readBytesAndClose(IOUtils.java:283)
at org.h2.value.ValueLobDb.getBytesNoCopy(ValueLobDb.java:327)

... 9 more

Caused by: java.lang.ArrayIndexOutOfBoundsException: 0
at org.h2.store.LobStorageBackend$LobInputStream.fillBuffer(
LobStorageBackend.java:787)
at org.h2.store.LobStorageBackend$LobInputStream.readFully(
LobStorageBackend.java:763)
at org.h2.store.LobStorageBackend$LobInputStream.read(
LobStorageBackend.java:754)
at org.h2.util.IOUtils.copy(IOUtils.java:168)

... 11 more

What can I do to avoid this problem?

-- 
You received this message because you are subscribed to the Google Groups "H2 
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to h2-database+unsubscr...@googlegroups.com.
To post to this group, send email to h2-database@googlegroups.com.
Visit this group at http://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/d/optout.
/*
 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.samples;

import java.io.ByteArrayInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * A very simple class that shows how to load the driver, create a database, create a table, and insert some data.
 */
public class HelloWorld {

    /**
     * Called when ran from command line.
     * 
     * @param args
     *            ignored
     */
    public static void main(String... args) throws Exception {
        Class.forName("org.h2.Driver");
        final int threads = 4;
        final int entries = 2000;
        init(entries);
        ExecutorService executor = Executors.newFixedThreadPool(threads);
        List<Future<?>> futures = new ArrayList<Future<?>>();

        for (int t = 0; t < threads; t++) {
            futures.add(executor.submit(new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    Connection conn = DriverManager.getConnection("jdbc:h2:~/test");
                    PreparedStatement ps = conn.prepareStatement("SELECT id,data FROM blobs WHERE id=?");
                    for (int i = 0; i < entries; i++) {
                        updateStamp(i, conn);
                        ps.setInt(1, i);
                        ResultSet rs = ps.executeQuery();
                        try {
                            while (rs.next()) {
                                byte[] data = rs.getBytes(2);
                                System.out.println(data.length);
                            }
                        } finally {
                            rs.close();
                        }
                    }
                    return null;
                }

                public synchronized void updateStamp(int id, Connection conn) throws SQLException {
                    PreparedStatement upd = conn.prepareStatement("UPDATE blobs SET stamp = ? WHERE id = ?");
                    try {
                        upd.setLong(1, System.currentTimeMillis());
                        upd.setInt(2, id);
                        upd.execute();
                    } finally {
                        upd.close();
                    }
                }
            }));
        }

        executor.shutdown();
        for (Future<?> f : futures)
            f.get();
    }

    /**
     * creates a simple 3 column table and fills it with random data
     */
    private static void init(int entries) throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:h2:~/test");
        conn.createStatement().execute("DROP TABLE IF EXISTS blobs");
        conn.createStatement().execute("CREATE TABLE blobs (id INTEGER, data BLOB, stamp BIGINT)");
        PreparedStatement ps = conn.prepareStatement("INSERT INTO blobs VALUES (?,?,?)");

        Random rand = new Random();
        for (int i = 0; i < entries; i++) {
            byte[] data = new byte[rand.nextInt(4096)];
            rand.nextBytes(data);

            ps.setInt(1, i);
            ps.setBlob(2, new ByteArrayInputStream(data));
            ps.setLong(3, 0);
            ps.execute();
        }
        ps.close();
    }

}

Reply via email to