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

MVStore: table engine

上级 3791fdca
......@@ -18,14 +18,23 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>MVStore: store the file header also at the end of each chunk,
<ul><li>New table engine "org.h2.mvstore.db.MVTableEngine"
that internally uses the MVStore to persist data.
To try it out, append ";DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine"
to the database URL.
This is still very experimental, and many features are not supported yet.
The data is stored in a file with the suffix ".mv.db".
</li><li>New connection setting "DEFAULT_TABLE_ENGINE" to use a specific
table engine if none is set explicitly. This is to simplify testing
the MVStore table engine.
</li><li>MVStore: store the file header also at the end of each chunk,
which results in a further reduced number of write operations.
</li><li>MVStore: a map implementation that supports concurrent operations.
</li><li>MVStore: unified exception handling; the version is included in the messages.
</li><li>MVStore: old data is now retained for 45 seconds by default.
</ul><li>MVStore: compress is now disabled by default, and can be enabled on request.
</ul><li>Support ALTER TABLE ADD ... AFTER. Patch from Andrew Gaul argaul@gmail.com. Fixes issue 401.
</ul><li>support "SELECT version()". Patch from Andrew Gaul argaul@gmail.com. Fixes issue 406.
</ul><li>Support ALTER TABLE ADD ... AFTER. Patch from Andrew Gaul (argaul at gmail.com). Fixes issue 401.
</ul><li>support "SELECT version()". Patch from Andrew Gaul. Fixes issue 406.
</ul><li>Improved OSGi support. H2 now registers itself as a DataSourceFactory service. Fixes issue 365.
</li></ul>
......
......@@ -5278,6 +5278,8 @@ public class Parser {
}
if (readIf("ENGINE")) {
command.setTableEngine(readUniqueIdentifier());
} else if (database.getSettings().defaultTableEngine != null) {
command.setTableEngine(database.getSettings().defaultTableEngine);
}
if (temp) {
if (readIf("ON")) {
......
......@@ -68,6 +68,11 @@ public class DropDatabase extends DefineCommand {
db.removeSchemaObject(session, t);
}
}
for (Table t : tables) {
if (t.getName() != null && Table.EXTERNAL_TABLE_ENGINE.equals(t.getTableType()) && !t.isHidden()) {
db.removeSchemaObject(session, t);
}
}
session.findLocalTempTable(null);
ArrayList<SchemaObject> list = New.arrayList();
list.addAll(db.getAllSchemaObjects(DbObject.SEQUENCE));
......
......@@ -423,6 +423,11 @@ public class Constants {
*/
public static final String SUFFIX_PAGE_FILE = ".h2.db";
/**
* The file name suffix of a MVStore file.
*/
public static final String SUFFIX_MV_FILE = ".mv.db";
/**
* The file name suffix of temporary files.
*/
......
......@@ -606,13 +606,15 @@ public class Session extends SessionWithState {
}
undoLog.add(log);
} else {
// see also UndoLogRecord.commit
ArrayList<Index> indexes = table.getIndexes();
for (int i = 0, size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
index.commit(operation, row);
if (database.isMultiVersion()) {
// see also UndoLogRecord.commit
ArrayList<Index> indexes = table.getIndexes();
for (int i = 0, size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
index.commit(operation, row);
}
row.commit();
}
row.commit();
}
}
......
......@@ -72,11 +72,12 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
}
/**
* Create a duplicate key exception with a message that contains the index name
* Create a duplicate key exception with a message that contains the index
* name.
*
* @return the exception
*/
DbException getDuplicateKeyException() {
protected DbException getDuplicateKeyException() {
String sql = getName() + " ON " + table.getSQL() + "(" + getColumnListSQL() + ")";
DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
e.setSource(this);
......
......@@ -203,7 +203,7 @@ public interface Index extends SchemaObject {
/**
* Commit the operation for a row. This is only important for multi-version
* indexes.
* indexes. The method is only called if multi-version is enabled.
*
* @param operation the operation type
* @param row the row
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import org.h2.engine.Session;
import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
/**
* An index that delegates indexing to another index.
*/
public class MVDelegateIndex extends BaseIndex {
private final MVPrimaryIndex mainIndex;
public MVDelegateIndex(MVTable table, int id, String name,
MVPrimaryIndex mainIndex,
IndexType indexType) {
IndexColumn[] cols = IndexColumn.wrap(new Column[] { table.getColumn(mainIndex.getMainIndexColumn())});
this.initBaseIndex(table, id, name, cols, indexType);
this.mainIndex = mainIndex;
if (!database.isPersistent() || id < 0) {
throw DbException.throwInternalError("" + name);
}
}
public void add(Session session, Row row) {
// nothing to do
}
public boolean canFindNext() {
return false;
}
public boolean canGetFirstOrLast() {
return false;
// TODO
// return true;
}
public void close(Session session) {
// nothing to do
}
public Cursor find(Session session, SearchRow first, SearchRow last) {
long min = mainIndex.getKey(first, Long.MIN_VALUE, Long.MIN_VALUE);
// ifNull is MIN_VALUE as well, because the column is never NULL
// so avoid returning all rows (returning one row is OK)
long max = mainIndex.getKey(last, Long.MAX_VALUE, Long.MIN_VALUE);
return mainIndex.find(session, min, max);
}
public Cursor findFirstOrLast(Session session, boolean first) {
return null;
// Cursor cursor;
// if (first) {
// cursor = mainIndex.find(session, Long.MIN_VALUE, Long.MAX_VALUE, false);
// } else {
// long x = mainIndex.getLastKey();
// cursor = mainIndex.find(session, x, x, false);
// }
// cursor.next();
// return cursor;
}
public Cursor findNext(Session session, SearchRow higherThan, SearchRow last) {
throw DbException.throwInternalError();
}
public int getColumnIndex(Column col) {
if (col.getColumnId() == mainIndex.getMainIndexColumn()) {
return 0;
}
return -1;
}
public double getCost(Session session, int[] masks) {
return 10 * getCostRangeIndex(masks, mainIndex.getRowCount(session));
}
public boolean needRebuild() {
return false;
}
public void remove(Session session, Row row) {
// nothing to do
}
public void remove(Session session) {
mainIndex.setMainIndexColumn(-1);
// TODO remove map?
// session.getDatabase().getPageStore().removeMeta(this, session);
}
public void truncate(Session session) {
// nothing to do
}
public void checkRename() {
// ok
}
public long getRowCount(Session session) {
return mainIndex.getRowCount(session);
}
public long getRowCountApproximation() {
return mainIndex.getRowCountApproximation();
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import java.util.Iterator;
import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.IndexColumn;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* A table stored in a MVStore.
*/
public class MVPrimaryIndex extends BaseIndex {
protected final MVTable mvTable;
protected MVMap<Long, Value[]> map;
private long nextKey;
private int mainIndexColumn = -1;
public MVPrimaryIndex(Database db, MVTable table, int id, IndexColumn[] columns,
IndexType indexType) {
this.mvTable = table;
initBaseIndex(table, id, table.getName() + "_DATA", columns, indexType);
int[] sortTypes = new int[columns.length];
for (int i = 0; i < columns.length; i++) {
sortTypes[i] = SortOrder.ASCENDING;
}
ValueArrayDataType t = new ValueArrayDataType(
db.getCompareMode(), db, sortTypes);
map = new MVMap<Long, Value[]>(new ObjectDataType(), t);
map = table.getStore().openMap(getName(), map);
Long k = map.lastKey();
nextKey = k == null ? 0 : k + 1;
}
public String getCreateSQL() {
return null;
}
public String getPlanSQL() {
return table.getSQL() + ".tableScan";
}
public void setMainIndexColumn(int mainIndexColumn) {
this.mainIndexColumn = mainIndexColumn;
}
public int getMainIndexColumn() {
return mainIndexColumn;
}
@Override
public void close(Session session) {
// ok
}
@Override
public void add(Session session, Row row) {
if (mainIndexColumn == -1) {
row.setKey(nextKey++);
} else {
Long c = row.getValue(mainIndexColumn).getLong();
row.setKey(c);
}
Value[] array = new Value[columns.length];
for (int i = 0; i < array.length; i++) {
array[i] = row.getValue(i);
}
if (map.containsKey(row.getKey())) {
String sql = "PRIMARY KEY ON " + table.getSQL();
if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) {
sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")";
}
DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
e.setSource(this);
throw e;
}
map.put(row.getKey(), array);
}
@Override
public void remove(Session session, Row row) {
Value[] old = map.remove(row.getKey());
if (old == null) {
throw DbException.get(ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1,
getSQL() + ": " + row.getKey());
}
}
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
long min, max;
if (first == null || mainIndexColumn < 0) {
min = Long.MIN_VALUE;
} else {
Value v = first.getValue(mainIndexColumn);
if (v == null) {
min = 0;
} else {
min = v.getLong();
}
}
if (last == null || mainIndexColumn < 0) {
max = Long.MAX_VALUE;
} else {
Value v = last.getValue(mainIndexColumn);
if (v == null) {
max = Long.MAX_VALUE;
} else {
max = v.getLong();
}
}
return new MVStoreCursor(session, map.keyIterator(min), max);
}
public MVTable getTable() {
return mvTable;
}
public Row getRow(Session session, long key) {
Value[] array = map.get(key);
Row row = new Row(array, 0);
row.setKey(key);
return row;
}
@Override
public double getCost(Session session, int[] masks) {
long cost = 10 * (map.getSize() + Constants.COST_ROW_OFFSET);
return cost;
}
@Override
public void remove(Session session) {
if (!map.isClosed()) {
map.removeMap();
}
}
@Override
public void truncate(Session session) {
map.clear();
}
@Override
public boolean canGetFirstOrLast() {
return false;
}
@Override
public Cursor findFirstOrLast(Session session, boolean first) {
// return first ? map.firstKey() : map.lastKey();
// TODO get first / last
return null;
}
@Override
public boolean needRebuild() {
// TODO Auto-generated method stub
return false;
}
@Override
public long getRowCount(Session session) {
return map.getSize();
}
@Override
public long getRowCountApproximation() {
return map.getSize();
}
@Override
public void checkRename() {
// ok
}
/**
* Get the key from the row.
*
* @param row the row
* @param ifEmpty the value to use if the row is empty
* @param ifNull the value to use if the column is NULL
* @return the key
*/
long getKey(SearchRow row, long ifEmpty, long ifNull) {
if (row == null) {
return ifEmpty;
}
Value v = row.getValue(mainIndexColumn);
if (v == null) {
throw DbException.throwInternalError(row.toString());
} else if (v == ValueNull.INSTANCE) {
return ifNull;
}
return v.getLong();
}
/**
* Search for a specific row or a set of rows.
*
* @param session the session
* @param first the key of the first row
* @param last the key of the last row
* @return the cursor
*/
Cursor find(Session session, long first, long last) {
return new MVStoreCursor(session, map.keyIterator(first), last);
}
/**
* A cursor.
*/
class MVStoreCursor implements Cursor {
private final Session session;
private final Iterator<Long> it;
private final long last;
private Long current;
private Row row;
public MVStoreCursor(Session session, Iterator<Long> it, long last) {
this.session = session;
this.it = it;
this.last = last;
}
@Override
public Row get() {
if (row == null) {
row = getRow(session, current);
}
return row;
}
@Override
public SearchRow getSearchRow() {
return get();
}
@Override
public boolean next() {
current = it.next();
if (current != null && current > last) {
current = null;
}
row = null;
return current != null;
}
@Override
public boolean previous() {
// TODO previous
return false;
}
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import java.util.Iterator;
import org.h2.constant.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.value.Value;
import org.h2.value.ValueLong;
/**
* A table stored in a MVStore.
*/
public class MVSecondaryIndex extends BaseIndex {
protected final MVTable mvTable;
protected final int keyColumns;
protected MVMap<Value[], Long> map;
public MVSecondaryIndex(Database db, MVTable table, int id, String indexName,
IndexColumn[] columns, IndexType indexType) {
this.mvTable = table;
initBaseIndex(table, id, indexName, columns, indexType);
// always store the row key in the map key,
// even for unique indexes, as some of the index columns could be null
keyColumns = columns.length + 1;
int[] sortTypes = new int[keyColumns];
for (int i = 0; i < columns.length; i++) {
sortTypes[i] = columns[i].sortType;
}
sortTypes[keyColumns - 1] = SortOrder.ASCENDING;
ValueArrayDataType t = new ValueArrayDataType(
db.getCompareMode(), db, sortTypes);
map = new MVMap<Value[], Long>(t, new ObjectDataType());
map = table.getStore().openMap(getName(), map);
}
@Override
public void close(Session session) {
// ok
}
@Override
public void add(Session session, Row row) {
Value[] array = getKey(row);
if (indexType.isUnique()) {
array[keyColumns - 1] = ValueLong.get(0);
if (map.containsKey(array)) {
throw getDuplicateKeyException();
}
}
array[keyColumns - 1] = ValueLong.get(row.getKey());
map.put(array, Long.valueOf(0));
}
@Override
public void remove(Session session, Row row) {
Value[] array = getKey(row);
Long old = map.remove(array);
if (old == null) {
if (old == null) {
throw DbException.get(ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1,
getSQL() + ": " + row.getKey());
}
}
}
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
Value[] min = getKey(first);
return new MVStoreCursor(session, map.keyIterator(min), last);
}
private Value[] getKey(SearchRow r) {
if (r == null) {
return null;
}
Value[] array = new Value[keyColumns];
for (int i = 0; i < columns.length; i++) {
Column c = columns[i];
int idx = c.getColumnId();
if (r != null) {
array[i] = r.getValue(idx);
}
}
array[keyColumns - 1] = ValueLong.get(r.getKey());
return array;
}
public MVTable getTable() {
return mvTable;
}
@Override
public double getCost(Session session, int[] masks) {
return 10 * getCostRangeIndex(masks, map.getSize());
}
@Override
public void remove(Session session) {
if (!map.isClosed()) {
map.removeMap();
}
}
@Override
public void truncate(Session session) {
map.clear();
}
@Override
public boolean canGetFirstOrLast() {
return false;
}
@Override
public Cursor findFirstOrLast(Session session, boolean first) {
return null;
}
@Override
public boolean needRebuild() {
// TODO there should be a better way
return map.getSize() == 0;
}
@Override
public long getRowCount(Session session) {
return map.getSize();
}
@Override
public long getRowCountApproximation() {
return map.getSize();
}
@Override
public void checkRename() {
// ok
}
/**
* A cursor.
*/
class MVStoreCursor implements Cursor {
private final Session session;
private final Iterator<Value[]> it;
private final SearchRow last;
private Value[] current;
private SearchRow searchRow;
private Row row;
public MVStoreCursor(Session session, Iterator<Value[]> it, SearchRow last) {
this.session = session;
this.it = it;
this.last = last;
}
@Override
public Row get() {
if (row == null) {
row = mvTable.getRow(session, getSearchRow().getKey());
}
return row;
}
@Override
public SearchRow getSearchRow() {
if (searchRow == null) {
Value[] array = current;
Column[] cols = getColumns();
searchRow = mvTable.getTemplateRow();
searchRow.setKey((array[array.length - 1]).getLong());
for (int i = 0; i < array.length - 1; i++) {
Column c = cols[i];
int idx = c.getColumnId();
Value v = array[i];
searchRow.setValue(idx, v);
}
}
return searchRow;
}
@Override
public boolean next() {
current = it.next();
searchRow = null;
if (current != null) {
if (last != null && compareRows(getSearchRow(), last) > 0) {
searchRow = null;
current = null;
}
}
row = null;
return current != null;
}
@Override
public boolean previous() {
// TODO previous
return false;
}
}
}
差异被折叠。
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import java.util.ArrayList;
import java.util.Map;
import java.util.WeakHashMap;
import org.h2.api.TableEngine;
import org.h2.command.ddl.CreateTableData;
import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.message.DbException;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.MVStoreBuilder;
import org.h2.mvstore.type.DataTypeFactory;
import org.h2.table.TableBase;
import org.h2.upgrade.v1_1.util.New;
/**
* A table engine that internally uses the MVStore.
*/
public class MVTableEngine implements TableEngine {
static final Map<String, Store> STORES = new WeakHashMap<String, Store>();
@Override
public TableBase createTable(CreateTableData data) {
Database db = data.session.getDatabase();
String storeName = db.getDatabasePath();
MVStoreBuilder storeBuilder;
Store store;
DataTypeFactory f = new ValueDataTypeFactory(db.getCompareMode(), db);
if (storeName == null) {
storeBuilder = MVStoreBuilder.inMemory();
storeBuilder.with(f);
store = new Store(db, storeBuilder.open());
} else {
synchronized (STORES) {
store = STORES.get(storeName);
if (store == null) {
storeBuilder = MVStoreBuilder.fileBased(storeName + Constants.SUFFIX_MV_FILE);
storeBuilder.with(f);
store = new Store(db, storeBuilder.open());
STORES.put(storeName, store);
} else if (store.db != db) {
throw DbException.get(ErrorCode.DATABASE_ALREADY_OPEN_1, storeName);
}
}
}
MVTable table = new MVTable(data, storeName, store.store);
store.openTables.add(table);
table.init(data.session);
return table;
}
static void closeTable(String storeName, MVTable table) {
synchronized (STORES) {
Store store = STORES.get(storeName);
store.openTables.remove(table);
if (store.openTables.size() == 0) {
store.store.store();
store.store.close();
STORES.remove(storeName);
}
}
}
/**
* A store with open tables.
*/
static class Store {
final Database db;
final MVStore store;
final ArrayList<MVTable> openTables = New.arrayList();
public Store(Database db, MVStore store) {
this.db = db;
this.store = store;
}
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import org.h2.mvstore.type.DataType;
import org.h2.mvstore.type.DataTypeFactory;
import org.h2.store.DataHandler;
import org.h2.value.CompareMode;
/**
* A data type factory for rows.
*/
public class ValueDataTypeFactory implements DataTypeFactory {
private final CompareMode compareMode;
private final DataHandler handler;
private DataTypeFactory parent;
ValueDataTypeFactory(CompareMode compareMode, DataHandler handler) {
this.compareMode = compareMode;
this.handler = handler;
}
@Override
public void setParent(DataTypeFactory parent) {
this.parent = parent;
}
@Override
public DataType buildDataType(String s) {
if (s.startsWith(ValueArrayDataType.PREFIX)) {
return ValueArrayDataType.fromString(compareMode, handler, s);
}
return parent.buildDataType(s);
}
@Override
public String getDataType(Class<?> objectClass) {
return parent.getDataType(objectClass);
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version 1.0,
and under the Eclipse Public License, Version 1.0
(http://h2database.com/html/license.html).
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Helper classes to use the MVStore in the H2 database.
</p></body></html>
\ No newline at end of file
......@@ -137,7 +137,7 @@ public class DbContents {
}
schemas[i] = schema;
String[] tableTypes = { "TABLE", "SYSTEM TABLE", "VIEW", "SYSTEM VIEW",
"TABLE LINK", "SYNONYM" };
"TABLE LINK", "SYNONYM", "EXTERNAL" };
schema.readTables(meta, tableTypes);
}
if (defaultSchema == null) {
......
......@@ -873,7 +873,7 @@ public class Data {
* @param handler the data handler for lobs
* @return the number of bytes required to store this value
*/
private static int getValueLen(Value v, DataHandler handler) {
public static int getValueLen(Value v, DataHandler handler) {
if (v == ValueNull.INSTANCE) {
return 1;
}
......
......@@ -86,6 +86,8 @@ public class FileLister {
ok = true;
} else if (f.endsWith(Constants.SUFFIX_PAGE_FILE)) {
ok = true;
} else if (f.endsWith(Constants.SUFFIX_MV_FILE)) {
ok = true;
} else if (all) {
if (f.endsWith(Constants.SUFFIX_LOCK_FILE)) {
ok = true;
......
......@@ -111,11 +111,11 @@ public class RegularTable extends TableBase {
}
public void addRow(Session session, Row row) {
int i = 0;
lastModificationId = database.getNextModificationDataId();
if (database.isMultiVersion()) {
row.setSessionId(session.getId());
}
int i = 0;
try {
for (int size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
......
......@@ -28,6 +28,7 @@ import org.h2.engine.DbObject;
import org.h2.engine.MetaRecord;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.mvstore.MVStoreTool;
import org.h2.result.Row;
import org.h2.result.SimpleRow;
import org.h2.security.SHA256;
......@@ -241,6 +242,10 @@ public class Recover extends Tool implements DataHandler {
dumpPageStore(fileName);
} else if (fileName.endsWith(Constants.SUFFIX_LOB_FILE)) {
dumpLob(fileName, false);
} else if (fileName.endsWith(Constants.SUFFIX_MV_FILE)) {
PrintWriter writer = getWriter(fileName, ".mv.txt");
MVStoreTool.dump(fileName, writer);
writer.close();
}
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论