提交 344f2633 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVTableEngine

上级 27d8e115
...@@ -25,7 +25,8 @@ Change Log ...@@ -25,7 +25,8 @@ Change Log
is no longer supported. This is to simplify the MVTableEngine. is no longer supported. This is to simplify the MVTableEngine.
</li><li>New column "information_schema.tables.row_count_estimate". </li><li>New column "information_schema.tables.row_count_estimate".
</li><li>Issue 468: trunc(timestamp) could return the wrong value. </li><li>Issue 468: trunc(timestamp) could return the wrong value.
</li><li><li>Fix deadlock when updating LOB's concurrently. See TestLob.testDeadlock2(). </li><li>Fixed a deadlock when updating LOB's concurrently. See TestLob.testDeadlock2().
</li><li>Fixed a deadlock related to very large temporary result sets.
</li></ul> </li></ul>
<h2>Version 1.3.172 (2013-05-25)</h2> <h2>Version 1.3.172 (2013-05-25)</h2>
......
...@@ -100,6 +100,7 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -100,6 +100,7 @@ public class Insert extends Prepared implements ResultTarget {
if (listSize > 0) { if (listSize > 0) {
int columnLen = columns.length; int columnLen = columns.length;
for (int x = 0; x < listSize; x++) { for (int x = 0; x < listSize; x++) {
session.startStatementWithinTransaction();
Row newRow = table.getTemplateRow(); Row newRow = table.getTemplateRow();
Expression[] expr = list.get(x); Expression[] expr = list.get(x);
setCurrentRowNumber(x + 1); setCurrentRowNumber(x + 1);
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
package org.h2.engine; package org.h2.engine;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
...@@ -646,6 +645,9 @@ public class Database implements DataHandler { ...@@ -646,6 +645,9 @@ public class Database implements DataHandler {
for (MetaRecord rec : records) { for (MetaRecord rec : records) {
rec.execute(this, systemSession, eventListener); rec.execute(this, systemSession, eventListener);
} }
if (mvStore != null) {
mvStore.rollback();
}
recompileInvalidViews(systemSession); recompileInvalidViews(systemSession);
starting = false; starting = false;
if (!readOnly) { if (!readOnly) {
...@@ -1797,10 +1799,15 @@ public class Database implements DataHandler { ...@@ -1797,10 +1799,15 @@ public class Database implements DataHandler {
* Flush all pending changes to the transaction log. * Flush all pending changes to the transaction log.
*/ */
public synchronized void flush() { public synchronized void flush() {
if (readOnly || pageStore == null) { if (readOnly) {
return; return;
} }
pageStore.flushLog(); if (pageStore != null) {
pageStore.flushLog();
}
if (mvStore != null) {
mvStore.store();
}
} }
public void setEventListener(DatabaseEventListener eventListener) { public void setEventListener(DatabaseEventListener eventListener) {
...@@ -2276,6 +2283,9 @@ public class Database implements DataHandler { ...@@ -2276,6 +2283,9 @@ public class Database implements DataHandler {
pageStore.checkpoint(); pageStore.checkpoint();
} }
} }
if (mvStore != null) {
mvStore.store();
}
} }
getTempFileDeleter().deleteUnused(); getTempFileDeleter().deleteUnused();
} }
......
...@@ -67,6 +67,20 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -67,6 +67,20 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
} }
} }
/**
* Check that the index columns are not CLOB or BLOB.
*
* @param columns the columns
*/
protected static void checkIndexColumnTypes(IndexColumn[] columns) {
for (IndexColumn c : columns) {
int type = c.column.getType();
if (type == Value.CLOB || type == Value.BLOB) {
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, "Index on BLOB or CLOB column: " + c.column.getCreateSQL());
}
}
}
@Override @Override
public String getDropSQL() { public String getDropSQL() {
return null; return null;
......
...@@ -75,15 +75,6 @@ public class PageBtreeIndex extends PageIndex { ...@@ -75,15 +75,6 @@ public class PageBtreeIndex extends PageIndex {
memoryPerPage = (Constants.MEMORY_PAGE_BTREE + store.getPageSize()) >> 2; memoryPerPage = (Constants.MEMORY_PAGE_BTREE + store.getPageSize()) >> 2;
} }
private static void checkIndexColumnTypes(IndexColumn[] columns) {
for (IndexColumn c : columns) {
int type = c.column.getType();
if (type == Value.CLOB || type == Value.BLOB) {
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, "Index on BLOB or CLOB column: " + c.column.getCreateSQL());
}
}
}
@Override @Override
public void add(Session session, Row row) { public void add(Session session, Row row) {
if (trace.isDebugEnabled()) { if (trace.isDebugEnabled()) {
......
...@@ -31,6 +31,9 @@ public class TreeIndex extends BaseIndex { ...@@ -31,6 +31,9 @@ public class TreeIndex extends BaseIndex {
public TreeIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) { public TreeIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
initBaseIndex(table, id, indexName, columns, indexType); initBaseIndex(table, id, indexName, columns, indexType);
tableData = table; tableData = table;
if (!database.isStarting()) {
checkIndexColumnTypes(columns);
}
} }
@Override @Override
......
...@@ -535,7 +535,7 @@ public class MVStore { ...@@ -535,7 +535,7 @@ public class MVStore {
} }
} catch (Exception e) { } catch (Exception e) {
try { try {
close(false); closeFile(false);
} catch (Exception e2) { } catch (Exception e2) {
// ignore // ignore
} }
...@@ -668,10 +668,6 @@ public class MVStore { ...@@ -668,10 +668,6 @@ public class MVStore {
* written but not committed, this is rolled back. All open maps are closed. * written but not committed, this is rolled back. All open maps are closed.
*/ */
public void close() { public void close() {
close(true);
}
private void close(boolean shrinkIfPossible) {
if (closed) { if (closed) {
return; return;
} }
...@@ -681,6 +677,20 @@ public class MVStore { ...@@ -681,6 +677,20 @@ public class MVStore {
store(false); store(false);
} }
} }
closeFile(true);
}
/**
* Close the file and the store, without writing anything.
*/
public void closeImmediately() {
closeFile(false);
}
private void closeFile(boolean shrinkIfPossible) {
if (closed) {
return;
}
closed = true; closed = true;
if (file == null) { if (file == null) {
return; return;
......
...@@ -329,8 +329,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -329,8 +329,7 @@ public class MVPrimaryIndex extends BaseIndex {
return dataMap; return dataMap;
} }
Transaction t = mvTable.getTransaction(session); Transaction t = mvTable.getTransaction(session);
long savepoint = session.getStatementSavepoint(); return dataMap.getInstance(t, Long.MAX_VALUE);
return dataMap.getInstance(t, savepoint);
} }
/** /**
......
...@@ -69,15 +69,6 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -69,15 +69,6 @@ public class MVSecondaryIndex extends BaseIndex {
dataMap = mvTable.getTransaction(null).openMap(mapName, mapBuilder); dataMap = mvTable.getTransaction(null).openMap(mapName, mapBuilder);
} }
private static void checkIndexColumnTypes(IndexColumn[] columns) {
for (IndexColumn c : columns) {
int type = c.column.getType();
if (type == Value.CLOB || type == Value.BLOB) {
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, "Index on BLOB or CLOB column: " + c.column.getCreateSQL());
}
}
}
@Override @Override
public void close(Session session) { public void close(Session session) {
// ok // ok
...@@ -98,7 +89,7 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -98,7 +89,7 @@ public class MVSecondaryIndex extends BaseIndex {
ValueArray array = getKey(row); ValueArray array = getKey(row);
if (indexType.isUnique()) { if (indexType.isUnique()) {
array.getList()[keyColumns - 1] = ValueLong.get(Long.MIN_VALUE); array.getList()[keyColumns - 1] = ValueLong.get(Long.MIN_VALUE);
ValueArray key = (ValueArray) map.ceilingKey(array); ValueArray key = (ValueArray) map.getLatestCeilingKey(array);
if (key != null) { if (key != null) {
SearchRow r2 = getRow(key.getList()); SearchRow r2 = getRow(key.getList());
if (compareRows(row, r2) == 0) { if (compareRows(row, r2) == 0) {
...@@ -253,8 +244,7 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -253,8 +244,7 @@ public class MVSecondaryIndex extends BaseIndex {
return dataMap; return dataMap;
} }
Transaction t = mvTable.getTransaction(session); Transaction t = mvTable.getTransaction(session);
long savepoint = session.getStatementSavepoint(); return dataMap.getInstance(t, Long.MAX_VALUE);
return dataMap.getInstance(t, savepoint);
} }
/** /**
......
...@@ -6,8 +6,6 @@ ...@@ -6,8 +6,6 @@
*/ */
package org.h2.mvstore.db; package org.h2.mvstore.db;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -15,8 +13,9 @@ import org.h2.api.TableEngine; ...@@ -15,8 +13,9 @@ import org.h2.api.TableEngine;
import org.h2.command.ddl.CreateTableData; import org.h2.command.ddl.CreateTableData;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.message.DbException;
import org.h2.mvstore.MVStore; import org.h2.mvstore.MVStore;
import org.h2.mvstore.db.TransactionStore.Transaction;
import org.h2.table.RegularTable;
import org.h2.table.TableBase; import org.h2.table.TableBase;
import org.h2.util.New; import org.h2.util.New;
...@@ -28,6 +27,9 @@ public class MVTableEngine implements TableEngine { ...@@ -28,6 +27,9 @@ public class MVTableEngine implements TableEngine {
@Override @Override
public TableBase createTable(CreateTableData data) { public TableBase createTable(CreateTableData data) {
Database db = data.session.getDatabase(); Database db = data.session.getDatabase();
if (!data.persistData || (data.temporary && !data.persistIndexes)) {
return new RegularTable(data);
}
Store store = db.getMvStore(); Store store = db.getMvStore();
if (store == null) { if (store == null) {
byte[] key = db.getFilePasswordHash(); byte[] key = db.getFilePasswordHash();
...@@ -86,7 +88,7 @@ public class MVTableEngine implements TableEngine { ...@@ -86,7 +88,7 @@ public class MVTableEngine implements TableEngine {
this.db = db; this.db = db;
this.store = store; this.store = store;
this.transactionStore = new TransactionStore(store, this.transactionStore = new TransactionStore(store,
new ValueDataType(null, null, null)); new ValueDataType(null, db, null));
} }
public MVStore getStore() { public MVStore getStore() {
...@@ -128,14 +130,7 @@ public class MVTableEngine implements TableEngine { ...@@ -128,14 +130,7 @@ public class MVTableEngine implements TableEngine {
if (store.isClosed()) { if (store.isClosed()) {
return; return;
} }
FileChannel f = store.getFile(); store.closeImmediately();
if (f != null) {
try {
f.close();
} catch (IOException e) {
throw DbException.convertIOException(e, "Closing file");
}
}
} }
/** /**
...@@ -154,6 +149,13 @@ public class MVTableEngine implements TableEngine { ...@@ -154,6 +149,13 @@ public class MVTableEngine implements TableEngine {
store.setWriteDelay(value); store.setWriteDelay(value);
} }
public void rollback() {
List<Transaction> list = transactionStore.getOpenTransactions();
for (Transaction t : list) {
t.rollback();
}
}
} }
} }
...@@ -60,6 +60,8 @@ public class TransactionStore { ...@@ -60,6 +60,8 @@ public class TransactionStore {
* transaction id. * transaction id.
*/ */
private final MVMap<String, String> settings; private final MVMap<String, String> settings;
private final DataType dataType;
private long lastTransactionIdStored; private long lastTransactionIdStored;
...@@ -80,23 +82,24 @@ public class TransactionStore { ...@@ -80,23 +82,24 @@ public class TransactionStore {
* Create a new transaction store. * Create a new transaction store.
* *
* @param store the store * @param store the store
* @param keyType the data type for map keys * @param dataType the data type for map keys and values
*/ */
public TransactionStore(MVStore store, DataType keyType) { public TransactionStore(MVStore store, DataType dataType) {
this.store = store; this.store = store;
this.dataType = dataType;
settings = store.openMap("settings"); settings = store.openMap("settings");
preparedTransactions = store.openMap("openTransactions", preparedTransactions = store.openMap("openTransactions",
new MVMap.Builder<Long, Object[]>()); new MVMap.Builder<Long, Object[]>());
// TODO commit of larger transaction could be faster if we have one undo // TODO commit of larger transaction could be faster if we have one undo
// log per transaction, or a range delete operation for maps // log per transaction, or a range delete operation for maps
VersionedValueType oldValueType = new VersionedValueType(keyType); VersionedValueType oldValueType = new VersionedValueType(dataType);
ArrayType valueType = new ArrayType(new DataType[]{ ArrayType undoLogValueType = new ArrayType(new DataType[]{
new ObjectDataType(), new ObjectDataType(), keyType, new ObjectDataType(), new ObjectDataType(), dataType,
oldValueType oldValueType
}); });
MVMap.Builder<long[], Object[]> builder = MVMap.Builder<long[], Object[]> builder =
new MVMap.Builder<long[], Object[]>(). new MVMap.Builder<long[], Object[]>().
valueType(valueType); valueType(undoLogValueType);
// TODO escape other map names, to avoid conflicts // TODO escape other map names, to avoid conflicts
undoLog = store.openMap("undoLog", builder); undoLog = store.openMap("undoLog", builder);
init(); init();
...@@ -229,10 +232,7 @@ public class TransactionStore { ...@@ -229,10 +232,7 @@ public class TransactionStore {
int opType = (Integer) op[0]; int opType = (Integer) op[0];
if (opType == Transaction.OP_REMOVE) { if (opType == Transaction.OP_REMOVE) {
int mapId = (Integer) op[1]; int mapId = (Integer) op[1];
Map<String, String> meta = store.getMetaMap(); MVMap<Object, VersionedValue> map = openMap(mapId);
String m = meta.get("map." + mapId);
String mapName = DataUtils.parseMap(m).get("name");
MVMap<Object, VersionedValue> map = store.openMap(mapName);
Object key = op[2]; Object key = op[2];
VersionedValue value = map.get(key); VersionedValue value = map.get(key);
// possibly the entry was added later on // possibly the entry was added later on
...@@ -246,6 +246,23 @@ public class TransactionStore { ...@@ -246,6 +246,23 @@ public class TransactionStore {
} }
endTransaction(t); endTransaction(t);
} }
private MVMap<Object, VersionedValue> openMap(int mapId) {
// TODO open map by id if possible
Map<String, String> meta = store.getMetaMap();
String m = meta.get("map." + mapId);
if (m == null) {
// the map was removed later on
return null;
}
String mapName = DataUtils.parseMap(m).get("name");
VersionedValueType vt = new VersionedValueType(dataType);
MVMap.Builder<Object, VersionedValue> mapBuilder =
new MVMap.Builder<Object, VersionedValue>().
keyType(dataType).valueType(vt);
MVMap<Object, VersionedValue> map = store.openMap(mapName, mapBuilder);
return map;
}
/** /**
* Check whether the given transaction id is still open and contains log * Check whether the given transaction id is still open and contains log
...@@ -304,19 +321,17 @@ public class TransactionStore { ...@@ -304,19 +321,17 @@ public class TransactionStore {
long[] undoKey = new long[] { t.getId(), logId }; long[] undoKey = new long[] { t.getId(), logId };
Object[] op = undoLog.get(undoKey); Object[] op = undoLog.get(undoKey);
int mapId = ((Integer) op[1]).intValue(); int mapId = ((Integer) op[1]).intValue();
// TODO open map by id if possible MVMap<Object, VersionedValue> map = openMap(mapId);
Map<String, String> meta = store.getMetaMap(); if (map != null) {
String m = meta.get("map." + mapId); Object key = op[2];
String mapName = DataUtils.parseMap(m).get("name"); VersionedValue oldValue = (VersionedValue) op[3];
MVMap<Object, VersionedValue> map = store.openMap(mapName); if (oldValue == null) {
Object key = op[2]; // this transaction added the value
VersionedValue oldValue = (VersionedValue) op[3]; map.remove(key);
if (oldValue == null) { } else {
// this transaction added the value // this transaction updated the value
map.remove(key); map.put(key, oldValue);
} else { }
// this transaction updated the value
map.put(key, oldValue);
} }
undoLog.remove(undoKey); undoLog.remove(undoKey);
} }
...@@ -339,8 +354,12 @@ public class TransactionStore { ...@@ -339,8 +354,12 @@ public class TransactionStore {
// TODO open map by id if possible // TODO open map by id if possible
Map<String, String> meta = store.getMetaMap(); Map<String, String> meta = store.getMetaMap();
String m = meta.get("map." + mapId); String m = meta.get("map." + mapId);
String mapName = DataUtils.parseMap(m).get("name"); if (m == null) {
set.add(mapName); // map was removed later on
} else {
String mapName = DataUtils.parseMap(m).get("name");
set.add(mapName);
}
} }
return set; return set;
} }
...@@ -939,6 +958,68 @@ public class TransactionStore { ...@@ -939,6 +958,68 @@ public class TransactionStore {
// TODO transactional lastKey // TODO transactional lastKey
return map.lastKey(); return map.lastKey();
} }
/**
* Get the most recent smallest key that is larger or equal to this key.
*
* @param key the key (may not be null)
* @return the result
*/
public K getLatestCeilingKey(K key) {
Cursor<K> cursor = map.keyIterator(key);
while (cursor.hasNext()) {
key = cursor.next();
if (get(key, Long.MAX_VALUE) != null) {
return key;
}
}
return null;
}
/**
* Get the smallest key that is larger or equal to this key.
*
* @param key the key (may not be null)
* @return the result
*/
public K ceilingKey(K key) {
int test;
// TODO this method is slow
Cursor<K> cursor = map.keyIterator(key);
while (cursor.hasNext()) {
key = cursor.next();
if (get(key) != null) {
return key;
}
}
return null;
// TODO transactional ceilingKey
// return map.ceilingKey(key);
}
/**
* Get the smallest key that is larger than the given key, or null if no
* such key exists.
*
* @param key the key (may not be null)
* @return the result
*/
public K higherKey(K key) {
// TODO transactional higherKey
return map.higherKey(key);
}
/**
* Get the largest key that is smaller than the given key, or null if no
* such key exists.
*
* @param key the key (may not be null)
* @return the result
*/
public K lowerKey(K key) {
// TODO Auto-generated method stub
return map.lowerKey(key);
}
/** /**
* Iterate over all keys. * Iterate over all keys.
...@@ -985,41 +1066,6 @@ public class TransactionStore { ...@@ -985,41 +1066,6 @@ public class TransactionStore {
}; };
} }
/**
* Get the smallest key that is larger or equal to this key.
*
* @param key the key (may not be null)
* @return the result
*/
public K ceilingKey(K key) {
// TODO transactional ceilingKey
return map.ceilingKey(key);
}
/**
* Get the smallest key that is larger than the given key, or null if no
* such key exists.
*
* @param key the key (may not be null)
* @return the result
*/
public K higherKey(K key) {
// TODO transactional higherKey
return map.higherKey(key);
}
/**
* Get the largest key that is smaller than the given key, or null if no
* such key exists.
*
* @param key the key (may not be null)
* @return the result
*/
public K lowerKey(K key) {
// TODO Auto-generated method stub
return map.lowerKey(key);
}
public Transaction getTransaction() { public Transaction getTransaction() {
return transaction; return transaction;
} }
......
...@@ -183,7 +183,7 @@ public class FilePathMem extends FilePath { ...@@ -183,7 +183,7 @@ public class FilePathMem extends FilePath {
} }
private boolean isRoot() { private boolean isRoot() {
return name.equals(getScheme()); return name.equals(getScheme() + ":");
} }
private static String getCanonicalPath(String fileName) { private static String getCanonicalPath(String fileName) {
......
...@@ -6,6 +6,11 @@ ...@@ -6,6 +6,11 @@
*/ */
package org.h2.test; package org.h2.test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Properties; import java.util.Properties;
import org.h2.Driver; import org.h2.Driver;
...@@ -493,7 +498,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -493,7 +498,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
private void runTests() throws SQLException { private void runTests() throws SQLException {
int test; int test;
mvStore = false; // mvStore = true;
smallLog = big = networked = memory = ssl = false; smallLog = big = networked = memory = ssl = false;
diskResult = traceSystemOut = diskUndo = false; diskResult = traceSystemOut = diskUndo = false;
...@@ -583,7 +588,9 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -583,7 +588,9 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestDeadlock().runTest(this); new TestDeadlock().runTest(this);
new TestEncryptedDb().runTest(this); new TestEncryptedDb().runTest(this);
new TestExclusive().runTest(this); new TestExclusive().runTest(this);
new TestFullText().runTest(this); if (!mvStore) {
new TestFullText().runTest(this);
}
new TestFunctionOverload().runTest(this); new TestFunctionOverload().runTest(this);
new TestFunctions().runTest(this); new TestFunctions().runTest(this);
new TestInit().runTest(this); new TestInit().runTest(this);
...@@ -599,13 +606,17 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -599,13 +606,17 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestMultiThreadedKernel().runTest(this); new TestMultiThreadedKernel().runTest(this);
new TestOpenClose().runTest(this); new TestOpenClose().runTest(this);
new TestOptimizations().runTest(this); new TestOptimizations().runTest(this);
new TestOutOfMemory().runTest(this); if (!mvStore) {
new TestOutOfMemory().runTest(this);
}
new TestPowerOff().runTest(this); new TestPowerOff().runTest(this);
new TestQueryCache().runTest(this); new TestQueryCache().runTest(this);
new TestReadOnly().runTest(this); new TestReadOnly().runTest(this);
new TestRecursiveQueries().runTest(this); new TestRecursiveQueries().runTest(this);
new TestRights().runTest(this); new TestRights().runTest(this);
new TestRunscript().runTest(this); if (!mvStore) {
new TestRunscript().runTest(this);
}
new TestSQLInjection().runTest(this); new TestSQLInjection().runTest(this);
new TestSessionsLocks().runTest(this); new TestSessionsLocks().runTest(this);
new TestSelectCountNonNullColumn().runTest(this); new TestSelectCountNonNullColumn().runTest(this);
...@@ -614,9 +625,11 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -614,9 +625,11 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestSpeed().runTest(this); new TestSpeed().runTest(this);
new TestTableEngines().runTest(this); new TestTableEngines().runTest(this);
new TestTempTables().runTest(this); new TestTempTables().runTest(this);
new TestTransaction().runTest(this); if (!mvStore) {
new TestTriggersConstraints().runTest(this); new TestTransaction().runTest(this);
new TestTwoPhaseCommit().runTest(this); new TestTriggersConstraints().runTest(this);
new TestTwoPhaseCommit().runTest(this);
}
new TestView().runTest(this); new TestView().runTest(this);
new TestViewAlterTable().runTest(this); new TestViewAlterTable().runTest(this);
new TestViewDropView().runTest(this); new TestViewDropView().runTest(this);
...@@ -652,8 +665,10 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -652,8 +665,10 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestConnectionPool().runTest(this); new TestConnectionPool().runTest(this);
new TestDataSource().runTest(this); new TestDataSource().runTest(this);
new TestXA().runTest(this); new TestXA().runTest(this);
new TestXASimple().runTest(this); if (!mvStore) {
new TestXASimple().runTest(this);
}
// server // server
new TestAutoServer().runTest(this); new TestAutoServer().runTest(this);
new TestNestedLoop().runTest(this); new TestNestedLoop().runTest(this);
...@@ -663,9 +678,11 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -663,9 +678,11 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestMvcc1().runTest(this); new TestMvcc1().runTest(this);
new TestMvcc2().runTest(this); new TestMvcc2().runTest(this);
new TestMvcc3().runTest(this); new TestMvcc3().runTest(this);
new TestMvccMultiThreaded().runTest(this); if (!mvStore) {
new TestRowLocks().runTest(this); new TestMvccMultiThreaded().runTest(this);
new TestRowLocks().runTest(this);
}
// synth // synth
new TestBtreeIndex().runTest(this); new TestBtreeIndex().runTest(this);
new TestDiskFull().runTest(this); new TestDiskFull().runTest(this);
...@@ -700,7 +717,9 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -700,7 +717,9 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
// unit // unit
new TestAutoReconnect().runTest(this); new TestAutoReconnect().runTest(this);
new TestCache().runTest(this); if (!mvStore) {
new TestCache().runTest(this);
}
new TestClearReferences().runTest(this); new TestClearReferences().runTest(this);
new TestCollation().runTest(this); new TestCollation().runTest(this);
new TestCompress().runTest(this); new TestCompress().runTest(this);
...@@ -717,7 +736,6 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -717,7 +736,6 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestFileSystem().runTest(this); new TestFileSystem().runTest(this);
new TestIntArray().runTest(this); new TestIntArray().runTest(this);
new TestIntIntHashMap().runTest(this); new TestIntIntHashMap().runTest(this);
// verify
new TestJmx().runTest(this); new TestJmx().runTest(this);
new TestMathUtils().runTest(this); new TestMathUtils().runTest(this);
new TestModifyOnWrite().runTest(this); new TestModifyOnWrite().runTest(this);
...@@ -726,12 +744,16 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -726,12 +744,16 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestObjectDeserialization().runTest(this); new TestObjectDeserialization().runTest(this);
new TestMultiThreadedKernel().runTest(this); new TestMultiThreadedKernel().runTest(this);
new TestOverflow().runTest(this); new TestOverflow().runTest(this);
new TestPageStore().runTest(this); if (!mvStore) {
new TestPageStore().runTest(this);
}
new TestPageStoreCoverage().runTest(this); new TestPageStoreCoverage().runTest(this);
new TestPattern().runTest(this); new TestPattern().runTest(this);
new TestPgServer().runTest(this); new TestPgServer().runTest(this);
new TestReader().runTest(this); new TestReader().runTest(this);
new TestRecovery().runTest(this); if (!mvStore) {
new TestRecovery().runTest(this);
}
new TestSampleApps().runTest(this); new TestSampleApps().runTest(this);
new TestScriptReader().runTest(this); new TestScriptReader().runTest(this);
runTest("org.h2.test.unit.TestServlet"); runTest("org.h2.test.unit.TestServlet");
...@@ -741,7 +763,9 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -741,7 +763,9 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestStreams().runTest(this); new TestStreams().runTest(this);
new TestStringCache().runTest(this); new TestStringCache().runTest(this);
new TestStringUtils().runTest(this); new TestStringUtils().runTest(this);
new TestTools().runTest(this); if (!mvStore) {
new TestTools().runTest(this);
}
new TestTraceSystem().runTest(this); new TestTraceSystem().runTest(this);
new TestUpgrade().runTest(this); new TestUpgrade().runTest(this);
new TestUtils().runTest(this); new TestUtils().runTest(this);
...@@ -749,6 +773,19 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -749,6 +773,19 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestValueHashMap().runTest(this); new TestValueHashMap().runTest(this);
new TestValueMemory().runTest(this); new TestValueMemory().runTest(this);
} }
public static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(obj);
return out.toByteArray();
}
public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is = new ObjectInputStream(in);
return is.readObject();
}
private void runTest(String className) { private void runTest(String className) {
try { try {
......
...@@ -247,6 +247,10 @@ public abstract class TestBase { ...@@ -247,6 +247,10 @@ public abstract class TestBase {
protected String getURL(String name, boolean admin) { protected String getURL(String name, boolean admin) {
String url; String url;
if (name.startsWith("jdbc:")) { if (name.startsWith("jdbc:")) {
if (config.mvStore) {
name = addOption(name, "DEFAULT_TABLE_ENGINE",
"org.h2.mvstore.db.MVTableEngine");
}
return name; return name;
} }
if (config.memory) { if (config.memory) {
......
...@@ -277,10 +277,14 @@ public class TestLob extends TestBase { ...@@ -277,10 +277,14 @@ public class TestLob extends TestBase {
conn2.close(); conn2.close();
} }
/**
* A background task.
*/
private final class Deadlock2Task1 extends Task { private final class Deadlock2Task1 extends Task {
public final Connection conn; public final Connection conn;
private Deadlock2Task1() throws SQLException { Deadlock2Task1() throws SQLException {
this.conn = getDeadlock2Connection(); this.conn = getDeadlock2Connection();
} }
...@@ -294,7 +298,9 @@ public class TestLob extends TestBase { ...@@ -294,7 +298,9 @@ public class TestLob extends TestBase {
ResultSet rs = stat.executeQuery("select name from test where id = " + random.nextInt(999)); ResultSet rs = stat.executeQuery("select name from test where id = " + random.nextInt(999));
if (rs.next()) { if (rs.next()) {
Reader r = rs.getClob("name").getCharacterStream(); Reader r = rs.getClob("name").getCharacterStream();
while ( r.read(tmp) > 0) {} while (r.read(tmp) > 0) {
// ignore
}
r.close(); r.close();
} }
rs.close(); rs.close();
...@@ -315,12 +321,17 @@ public class TestLob extends TestBase { ...@@ -315,12 +321,17 @@ public class TestLob extends TestBase {
} }
} }
} }
} }
/**
* A background task.
*/
private final class Deadlock2Task2 extends Task { private final class Deadlock2Task2 extends Task {
public final Connection conn; public final Connection conn;
private Deadlock2Task2() throws SQLException { Deadlock2Task2() throws SQLException {
this.conn = getDeadlock2Connection(); this.conn = getDeadlock2Connection();
} }
...@@ -332,6 +343,7 @@ public class TestLob extends TestBase { ...@@ -332,6 +343,7 @@ public class TestLob extends TestBase {
stat.execute("update test set counter = " + random.nextInt(10) + " where id = " + random.nextInt(1000)); stat.execute("update test set counter = " + random.nextInt(10) + " where id = " + random.nextInt(1000));
} }
} }
} }
private void testDeadlock2() throws Exception { private void testDeadlock2() throws Exception {
...@@ -370,7 +382,7 @@ public class TestLob extends TestBase { ...@@ -370,7 +382,7 @@ public class TestLob extends TestBase {
conn.close(); conn.close();
} }
private Connection getDeadlock2Connection() throws SQLException { Connection getDeadlock2Connection() throws SQLException {
return getConnection("lob;MULTI_THREADED=TRUE;LOCK_TIMEOUT=60000"); return getConnection("lob;MULTI_THREADED=TRUE;LOCK_TIMEOUT=60000");
} }
...@@ -473,15 +485,12 @@ public class TestLob extends TestBase { ...@@ -473,15 +485,12 @@ public class TestLob extends TestBase {
Statement stat; Statement stat;
conn = getConnection("lob"); conn = getConnection("lob");
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute("create memory table test(x clob unique)"); try {
stat.execute("insert into test values('hello')"); stat.execute("create memory table test(x clob unique)");
stat.execute("insert into test values('world')"); fail();
assertThrows(ErrorCode.DUPLICATE_KEY_1, stat). } catch (SQLException e) {
execute("insert into test values('world')"); assertEquals(ErrorCode.FEATURE_NOT_SUPPORTED_1, e.getErrorCode());
stat.execute("insert into test values(space(10000) || 'a')"); }
assertThrows(ErrorCode.DUPLICATE_KEY_1, stat).
execute("insert into test values(space(10000) || 'a')");
stat.execute("insert into test values(space(10000) || 'b')");
conn.close(); conn.close();
} }
......
...@@ -23,6 +23,7 @@ import org.h2.engine.Database; ...@@ -23,6 +23,7 @@ import org.h2.engine.Database;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Restore;
import org.h2.util.Task; import org.h2.util.Task;
/** /**
...@@ -42,6 +43,7 @@ public class TestMVTableEngine extends TestBase { ...@@ -42,6 +43,7 @@ public class TestMVTableEngine extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
// testSpeed(); // testSpeed();
testRollbackAfterCrash();
testReferentialIntegrity(); testReferentialIntegrity();
testWriteDelay(); testWriteDelay();
testAutoCommit(); testAutoCommit();
...@@ -109,13 +111,91 @@ int test; ...@@ -109,13 +111,91 @@ int test;
//System.out.println(prof.getTop(10)); //System.out.println(prof.getTop(10));
System.out.println((System.currentTimeMillis() - time) + " " + dbName + " after"); System.out.println((System.currentTimeMillis() - time) + " " + dbName + " after");
} }
private void testRollbackAfterCrash() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true);
Connection conn;
Statement stat;
String url = "mvstore;default_table_engine=org.h2.mvstore.db.MVTableEngine";
String url2 = "mvstore2;default_table_engine=org.h2.mvstore.db.MVTableEngine";
conn = getConnection(url);
stat = conn.createStatement();
stat.execute("create table test(id int)");
stat.execute("insert into test values(0)");
stat.execute("set write_delay 0");
conn.setAutoCommit(false);
stat.execute("insert into test values(1)");
stat.execute("shutdown immediately");
conn = getConnection(url);
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select row_count_estimate " +
"from information_schema.tables where table_name='TEST'");
rs.next();
assertEquals(1, rs.getLong(1));
stat.execute("drop table test");
stat.execute("create table test(id int primary key, data clob)");
stat.execute("insert into test values(1, space(10000))");
conn.setAutoCommit(false);
stat.execute("delete from test");
stat.execute("checkpoint");
stat.execute("shutdown immediately");
conn = getConnection(url);
stat = conn.createStatement();
rs = stat.executeQuery("select * from test");
assertTrue(rs.next());
stat.execute("drop all objects delete files");
conn.close();
conn = getConnection(url);
stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar)");
stat.execute("create index idx_name on test(name, id)");
stat.execute("insert into test select x, x || space(200 * x) from system_range(1, 10)");
conn.setAutoCommit(false);
stat.execute("delete from test where id > 5");
stat.execute("backup to '" + getBaseDir() + "/backup.zip'");
conn.rollback();
Restore.execute(getBaseDir() + "/backup.zip", getBaseDir(), "mvstore2");
Connection conn2;
conn2 = getConnection(url2);
conn.close();
conn2.close();
}
private void testReferentialIntegrity() throws Exception { private void testReferentialIntegrity() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
Connection conn; Connection conn;
Statement stat; Statement stat;
conn = getConnection("mvstore;default_table_engine=org.h2.mvstore.db.MVTableEngine"); conn = getConnection("mvstore;default_table_engine=org.h2.mvstore.db.MVTableEngine");
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute("create table test(id int, parent int " +
"references test(id) on delete cascade)");
stat.execute("insert into test values(0, 0)");
stat.execute("delete from test");
stat.execute("drop table test");
stat.execute("create table parent(id int, name varchar)");
stat.execute("create table child(id int, parentid int, " +
"foreign key(parentid) references parent(id))");
stat.execute("insert into parent values(1, 'mary'), (2, 'john')");
stat.execute("insert into child values(10, 1), (11, 1), (20, 2), (21, 2)");
stat.execute("update parent set name = 'marc' where id = 1");
stat.execute("merge into parent key(id) values(1, 'marcy')");
stat.execute("drop table parent, child");
stat.execute("create table test(id identity, parent bigint, " +
"foreign key(parent) references(id))");
stat.execute("insert into test values(0, 0), (1, NULL), " +
"(2, 1), (3, 3), (4, 3)");
stat.execute("drop table test");
stat.execute("create table parent(id int)"); stat.execute("create table parent(id int)");
stat.execute("create table child(pid int)"); stat.execute("create table child(pid int)");
...@@ -126,7 +206,7 @@ int test; ...@@ -126,7 +206,7 @@ int test;
"foreign key(pid) references parent(id)"); "foreign key(pid) references parent(id)");
fail(); fail();
} catch (SQLException e) { } catch (SQLException e) {
// expected assertEquals(ErrorCode.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, e.getErrorCode());
} }
stat.execute("update child set pid=1"); stat.execute("update child set pid=1");
stat.execute("drop table child, parent"); stat.execute("drop table child, parent");
...@@ -140,16 +220,15 @@ int test; ...@@ -140,16 +220,15 @@ int test;
"foreign key(pid) references parent(id)"); "foreign key(pid) references parent(id)");
fail(); fail();
} catch (SQLException e) { } catch (SQLException e) {
// expected assertEquals(ErrorCode.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, e.getErrorCode());
} }
stat.execute("drop table child, parent"); stat.execute("drop table child, parent");
// currently not supported, as previous rows are not visible stat.execute("create table test(id identity, parent bigint, " +
// stat.execute("create table test(id identity, parent bigint, "foreign key(parent) references(id))");
// foreign key(parent) references(id))"); stat.execute("insert into test values(0, 0), (1, NULL), " +
// stat.execute("insert into test values(0, 0), (1, NULL), "(2, 1), (3, 3), (4, 3)");
// (2, 1), (3, 3), (4, 3)"); stat.execute("drop table test");
// stat.execute("drop table test");
stat.execute("create table parent(id int, x int)"); stat.execute("create table parent(id int, x int)");
stat.execute("insert into parent values(1, 2)"); stat.execute("insert into parent values(1, 2)");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论