提交 a1c7cffb authored 作者: Thomas Mueller's avatar Thomas Mueller

When LOB objects are stored in the database (using the experimental setting…

When LOB objects are stored in the database (using the experimental setting h2.lobInDatabase), and using the MVCC mode, opening a database with uncommitted transactions could throw a NullPointerException.
上级 de67a02c
...@@ -2270,4 +2270,8 @@ public class Database implements DataHandler { ...@@ -2270,4 +2270,8 @@ public class Database implements DataHandler {
this.defaultTableType = defaultTableType; this.defaultTableType = defaultTableType;
} }
public void setMultiVersion(boolean multiVersion) {
this.multiVersion = multiVersion;
}
} }
...@@ -338,7 +338,13 @@ public class PageStore implements CacheWriter { ...@@ -338,7 +338,13 @@ public class PageStore implements CacheWriter {
readVariableHeader(); readVariableHeader();
log = new PageLog(this); log = new PageLog(this);
log.openForReading(logKey, logFirstTrunkPage, logFirstDataPage); log.openForReading(logKey, logFirstTrunkPage, logFirstDataPage);
boolean old = database.isMultiVersion();
// temporarily disabling multi-version concurrency, because
// the multi-version index sometimes compares rows
// and the LOB storage is not yet available.
database.setMultiVersion(false);
recover(); recover();
database.setMultiVersion(old);
if (!database.isReadOnly()) { if (!database.isReadOnly()) {
recoveryRunning = true; recoveryRunning = true;
log.free(); log.free();
...@@ -789,8 +795,7 @@ public class PageStore implements CacheWriter { ...@@ -789,8 +795,7 @@ public class PageStore implements CacheWriter {
trace.debug("getFirstUncommittedSection"); trace.debug("getFirstUncommittedSection");
Session[] sessions = database.getSessions(true); Session[] sessions = database.getSessions(true);
int firstUncommittedSection = log.getLogSectionId(); int firstUncommittedSection = log.getLogSectionId();
for (int i = 0; i < sessions.length; i++) { for (Session session : sessions) {
Session session = sessions[i];
int firstUncommitted = session.getFirstUncommittedLog(); int firstUncommitted = session.getFirstUncommittedLog();
if (firstUncommitted != Session.LOG_WRITTEN) { if (firstUncommitted != Session.LOG_WRITTEN) {
if (firstUncommitted < firstUncommittedSection) { if (firstUncommitted < firstUncommittedSection) {
...@@ -1589,7 +1594,7 @@ public class PageStore implements CacheWriter { ...@@ -1589,7 +1594,7 @@ public class PageStore implements CacheWriter {
throw DbException.throwInternalError(row.toString()); throw DbException.throwInternalError(row.toString());
} }
} }
for (int i = 0; i < columns.length; i++) { for (int i = 0, len = columns.length; i < len; i++) {
Column col = new Column("C" + i, Value.INT); Column col = new Column("C" + i, Value.INT);
data.columns.add(col); data.columns.add(col);
} }
...@@ -1612,8 +1617,9 @@ public class PageStore implements CacheWriter { ...@@ -1612,8 +1617,9 @@ public class PageStore implements CacheWriter {
} }
RegularTable table = (RegularTable) p.getTable(); RegularTable table = (RegularTable) p.getTable();
Column[] tableCols = table.getColumns(); Column[] tableCols = table.getColumns();
IndexColumn[] cols = new IndexColumn[columns.length]; int len = columns.length;
for (int i = 0; i < columns.length; i++) { IndexColumn[] cols = new IndexColumn[len];
for (int i = 0; i < len; i++) {
String c = columns[i]; String c = columns[i];
IndexColumn ic = new IndexColumn(); IndexColumn ic = new IndexColumn();
int idx = c.indexOf('/'); int idx = c.indexOf('/');
...@@ -1630,8 +1636,8 @@ public class PageStore implements CacheWriter { ...@@ -1630,8 +1636,8 @@ public class PageStore implements CacheWriter {
if (ops[3].equals("d")) { if (ops[3].equals("d")) {
indexType = IndexType.createPrimaryKey(true, false); indexType = IndexType.createPrimaryKey(true, false);
Column[] tableColumns = table.getColumns(); Column[] tableColumns = table.getColumns();
for (int i = 0; i < cols.length; i++) { for (IndexColumn indexColumn : cols) {
tableColumns[cols[i].column.getColumnId()].setNullable(false); tableColumns[indexColumn.column.getColumnId()].setNullable(false);
} }
} else { } else {
indexType = IndexType.createNonUnique(true); indexType = IndexType.createNonUnique(true);
......
...@@ -12,6 +12,7 @@ import java.sql.PreparedStatement; ...@@ -12,6 +12,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
...@@ -20,6 +21,7 @@ import org.h2.result.Row; ...@@ -20,6 +21,7 @@ import org.h2.result.Row;
import org.h2.store.Page; import org.h2.store.Page;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.New;
/** /**
* Test the page store. * Test the page store.
...@@ -35,10 +37,12 @@ public class TestPageStore extends TestBase implements DatabaseEventListener { ...@@ -35,10 +37,12 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
*/ */
public static void main(String... a) throws Exception { public static void main(String... a) throws Exception {
System.setProperty("h2.check2", "true"); System.setProperty("h2.check2", "true");
System.setProperty("h2.lobInDatabase", "true");
TestBase.createCaller().init().test(); TestBase.createCaller().init().test();
} }
public void test() throws Exception { public void test() throws Exception {
testRecoverLobInDatabase();
testWriteTransactionLogBeforeData(); testWriteTransactionLogBeforeData();
testDefrag(); testDefrag();
testInsertReverse(); testInsertReverse();
...@@ -68,6 +72,41 @@ public class TestPageStore extends TestBase implements DatabaseEventListener { ...@@ -68,6 +72,41 @@ public class TestPageStore extends TestBase implements DatabaseEventListener {
deleteDb("pageStore"); deleteDb("pageStore");
} }
private void testRecoverLobInDatabase() throws SQLException {
deleteDb("pageStore");
String url = getURL("pageStore;MVCC=TRUE;CACHE_SIZE=1", true);
Connection conn;
Statement stat;
conn = getConnection(url, getUser(), getPassword());
stat = conn.createStatement();
stat.execute("create table test(id int primary key, name clob)");
stat.execute("create index idx_id on test(id)");
stat.execute("insert into test select x, space(1100+x) from system_range(1, 100)");
Connection conn2;
Statement stat2;
Random r = new Random(1);
ArrayList<Connection> list = New.arrayList();
for (int i = 0; i < 10; i++) {
conn2 = getConnection(url, getUser(), getPassword());
list.add(conn2);
stat2 = conn.createStatement();
conn2.setAutoCommit(false);
if (r.nextBoolean()) {
stat2.execute("update test set id = id where id = " + r.nextInt(100));
} else {
stat2.execute("delete from test where id = " + r.nextInt(100));
}
}
stat.execute("shutdown immediately");
try {
conn.close();
} catch (SQLException e) {
// ignore
}
conn = getConnection(url, getUser(), getPassword());
conn.close();
}
private void testWriteTransactionLogBeforeData() throws SQLException { private void testWriteTransactionLogBeforeData() throws SQLException {
deleteDb("pageStore"); deleteDb("pageStore");
String url = getURL("pageStore;CACHE_SIZE=16;WRITE_DELAY=1000000", true); String url = getURL("pageStore;CACHE_SIZE=16;WRITE_DELAY=1000000", true);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论