提交 6e972a35 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: temporary tables from result sets could survive re-opening a database,…

MVStore: temporary tables from result sets could survive re-opening a database, which could result in a ClassCastException.
上级 d0d72f03
......@@ -27,10 +27,12 @@ import org.h2.mvstore.FileStore;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.db.TransactionStore.Transaction;
import org.h2.mvstore.db.TransactionStore.TransactionMap;
import org.h2.store.InDoubtTransaction;
import org.h2.store.fs.FileChannelInputStream;
import org.h2.store.fs.FileUtils;
import org.h2.table.TableBase;
import org.h2.util.BitField;
import org.h2.util.New;
/**
......@@ -230,12 +232,23 @@ public class MVTableEngine implements TableEngine {
/**
* Remove all temporary maps.
* @param objectIds
*/
public void removeTemporaryMaps() {
public void removeTemporaryMaps(BitField objectIds) {
for (String mapName : store.getMapNames()) {
if (mapName.startsWith("temp.")) {
MVMap<?, ?> map = store.openMap(mapName);
store.removeMap(map);
} else if (mapName.startsWith("table.") || mapName.startsWith("index.")) {
int id = Integer.parseInt(mapName.substring(1 + mapName.indexOf(".")));
if (!objectIds.get(id)) {
ValueDataType keyType = new ValueDataType(null, null, null);
ValueDataType valueType = new ValueDataType(null, null, null);
Transaction t = transactionStore.begin();
TransactionMap<?, ?> m = t.openMap(mapName, keyType, valueType);
transactionStore.removeMap(m);
t.commit();
}
}
}
}
......@@ -303,6 +316,9 @@ public class MVTableEngine implements TableEngine {
*/
public void compactFile(long maxCompactTime) {
store.setRetentionTime(0);
if (maxCompactTime == Long.MAX_VALUE) {
store.compactRewriteFully();
} else {
long start = System.currentTimeMillis();
while (store.compact(99, 4 * 1024 * 1024)) {
store.sync();
......@@ -311,6 +327,7 @@ public class MVTableEngine implements TableEngine {
break;
}
}
}
store.compactMoveChunks();
}
......
......@@ -447,8 +447,8 @@ public class TransactionStore {
// if there is no open transaction,
// and if there have been many changes, store them now
if (undoLog.isEmpty()) {
int unsaved = store.getUnsavedPageCount();
int max = store.getAutoCommitPageCount();
int unsaved = store.getUnsavedMemory();
int max = store.getAutoCommitMemory();
// save at 3/4 capacity
if (unsaved * 4 > max * 3) {
store.commit();
......@@ -1162,7 +1162,6 @@ public class TransactionStore {
*/
@SuppressWarnings("unchecked")
public V get(K key, long maxLogId) {
transaction.checkNotClosed();
VersionedValue data = getValue(key, maxLogId);
return data == null ? null : (V) data.value;
}
......
......@@ -64,7 +64,12 @@ public class LocalResult implements ResultInterface, ResultTarget {
if (session == null) {
this.maxMemoryRows = Integer.MAX_VALUE;
} else {
Database db = session.getDatabase();
if (db.isPersistent() && !db.isReadOnly()) {
this.maxMemoryRows = session.getDatabase().getMaxMemoryRows();
} else {
this.maxMemoryRows = Integer.MAX_VALUE;
}
}
rows = New.arrayList();
this.visibleColumnCount = visibleColumnCount;
......@@ -72,6 +77,10 @@ public class LocalResult implements ResultInterface, ResultTarget {
this.expressions = expressions;
}
public void setMaxMemoryRows(int maxValue) {
this.maxMemoryRows = maxValue;
}
/**
* Construct a local result set by reading all data from a regular result
* set.
......@@ -227,7 +236,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
@Override
public boolean next() {
if (rowId < rowCount) {
if (!closed && rowId < rowCount) {
rowId++;
if (rowId < rowCount) {
if (external != null) {
......@@ -259,10 +268,8 @@ public class LocalResult implements ResultInterface, ResultTarget {
ValueArray array = ValueArray.get(values);
distinctRows.put(array, values);
rowCount = distinctRows.size();
Database db = session.getDatabase();
if (rowCount > db.getMaxMemoryRows() &&
db.isPersistent() && !db.isReadOnly()) {
external = new ResultTempTable(session, sort);
if (rowCount > maxMemoryRows) {
external = new ResultTempTable(session, true, sort);
rowCount = external.addRows(distinctRows.values());
distinctRows = null;
}
......@@ -273,16 +280,9 @@ public class LocalResult implements ResultInterface, ResultTarget {
}
rows.add(values);
rowCount++;
if (rows.size() > maxMemoryRows && session.getDatabase().isPersistent()) {
if (rows.size() > maxMemoryRows) {
if (external == null) {
if (randomAccess) {
Database db = session.getDatabase();
if (!db.isReadOnly()) {
external = new ResultTempTable(session, sort);
}
} else {
external = new ResultDiskBuffer(session, sort, values.length);
}
external = new ResultTempTable(session, false, sort);
}
addRowsToDisk();
}
......@@ -319,14 +319,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
break;
}
if (external == null) {
if (randomAccess) {
Database db = session.getDatabase();
if (!db.isReadOnly()) {
external = new ResultTempTable(session, sort);
}
} else {
external = new ResultDiskBuffer(session, sort, list.length);
}
external = new ResultTempTable(session, true, sort);
}
rows.add(list);
if (rows.size() > maxMemoryRows) {
......
......@@ -29,6 +29,7 @@ import org.h2.value.ValueArray;
public class ResultTempTable implements ResultExternal {
private static final String COLUMN_NAME = "DATA";
private final boolean distinct;
private final SortOrder sort;
private final Index index;
private Session session;
......@@ -40,8 +41,9 @@ public class ResultTempTable implements ResultExternal {
private boolean closed;
private int childCount;
ResultTempTable(Session session, SortOrder sort) {
ResultTempTable(Session session, boolean distinct, SortOrder sort) {
this.session = session;
this.distinct = distinct;
this.sort = sort;
Schema schema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN);
Column column = new Column(COLUMN_NAME, Value.ARRAY);
......@@ -61,13 +63,14 @@ public class ResultTempTable implements ResultExternal {
indexColumn.column = column;
indexColumn.columnName = COLUMN_NAME;
IndexType indexType;
indexType = IndexType.createPrimaryKey(true, false);
IndexColumn[] indexCols = { indexColumn };
if (session.getDatabase().getMvStore() != null) {
indexType = IndexType.createNonUnique(true);
index = table.addIndex(session, data.tableName, indexId, indexCols,
indexType, true, null);
index.setTemporary(true);
} else {
indexType = IndexType.createPrimaryKey(true, false);
index = new PageBtreeIndex((RegularTable) table, indexId,
data.tableName, indexCols, indexType, true, session);
index.setTemporary(true);
......@@ -78,6 +81,7 @@ public class ResultTempTable implements ResultExternal {
private ResultTempTable(ResultTempTable parent) {
this.parent = parent;
this.distinct = parent.distinct;
this.session = parent.session;
this.table = parent.table;
this.index = parent.index;
......@@ -119,11 +123,16 @@ public class ResultTempTable implements ResultExternal {
@Override
public int addRow(Value[] values) {
Row row = convertToRow(values);
if (distinct) {
Cursor cursor = find(row);
if (cursor == null) {
table.addRow(session, row);
rowCount++;
}
} else {
table.addRow(session, row);
rowCount++;
}
return rowCount;
}
......@@ -199,14 +208,20 @@ public class ResultTempTable implements ResultExternal {
public Value[] next() {
if (resultCursor == null) {
if (session.getDatabase().getMvStore() != null) {
Index idx;
if (distinct || sort != null) {
idx = index;
} else {
idx = table.getScanIndex(session);
}
// sometimes the transaction is already committed,
// in which case we can't use the session
if (index.getRowCount(session) == 0 && rowCount > 0) {
if (idx.getRowCount(session) == 0 && rowCount > 0) {
// this means querying is not transactional
resultCursor = index.find((Session) null, null, null);
resultCursor = idx.find((Session) null, null, null);
} else {
// the transaction is still open
resultCursor = index.find(session, null, null);
resultCursor = idx.find(session, null, null);
}
} else {
resultCursor = index.find(session, null, null);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论