提交 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.Collections;
import java.util.Comparator;
import org.h2.api.DatabaseEventListener;
import org.h2.command.ddl.CreateTableData;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintReferential;
import org.h2.engine.Constants;
import org.h2.engine.DbObject;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.index.MultiVersionIndex;
import org.h2.message.DbException;
import org.h2.mvstore.MVStore;
import org.h2.result.Row;
import org.h2.result.SortOrder;
import org.h2.schema.SchemaObject;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableBase;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.value.Value;
/**
* A table stored in a MVStore.
*/
public class MVTable extends TableBase {
private final String storeName;
private final MVStore store;
private final boolean hidden;
private MVPrimaryIndex primaryIndex;
private ArrayList<Index> indexes = New.arrayList();
private long lastModificationId;
private long rowCount;
public MVTable(CreateTableData data, String storeName, MVStore store) {
super(data);
this.storeName = storeName;
this.store = store;
this.hidden = data.isHidden;
}
void init(Session session) {
primaryIndex = new MVPrimaryIndex(session.getDatabase(),
this, getId(),
IndexColumn.wrap(getColumns()),
IndexType.createScan(true)
);
rowCount = primaryIndex.getRowCount(session);
indexes.add(primaryIndex);
}
@Override
public void lock(Session session, boolean exclusive, boolean force) {
// TODO locking
}
@Override
public boolean isHidden() {
return hidden;
}
@Override
public boolean canTruncate() {
// TODO copy & pasted source code from RegularTable
if (getCheckForeignKeyConstraints() && database.getReferentialIntegrity()) {
ArrayList<Constraint> constraints = getConstraints();
if (constraints != null) {
for (int i = 0, size = constraints.size(); i < size; i++) {
Constraint c = constraints.get(i);
if (!(c.getConstraintType().equals(Constraint.REFERENTIAL))) {
continue;
}
ConstraintReferential ref = (ConstraintReferential) c;
if (ref.getRefTable() == this) {
return false;
}
}
}
}
return true;
}
@Override
public void unlock(Session s) {
// TODO locking
}
@Override
public boolean isLockedExclusively() {
// TODO locking
return false;
}
@Override
public void close(Session session) {
MVTableEngine.closeTable(storeName, this);
}
Row getRow(Session session, long key) {
return primaryIndex.getRow(session, key);
}
@Override
public Index addIndex(Session session, String indexName, int indexId,
IndexColumn[] cols, IndexType indexType, boolean create,
String indexComment) {
if (indexType.isPrimaryKey()) {
for (IndexColumn c : cols) {
Column column = c.column;
if (column.isNullable()) {
throw DbException.get(ErrorCode.COLUMN_MUST_NOT_BE_NULLABLE_1, column.getName());
}
column.setPrimaryKey(true);
}
}
boolean isSessionTemporary = isTemporary() && !isGlobalTemporary();
if (!isSessionTemporary) {
database.lockMeta(session);
}
Index index;
// TODO support in-memory indexes
// if (isPersistIndexes() && indexType.isPersistent()) {
int mainIndexColumn;
mainIndexColumn = getMainIndexColumn(indexType, cols);
// if (database.isStarting()) {
// mainIndexColumn = -1;
// } else if (!database.isStarting() && primaryIndex.getRowCount(session) != 0) {
// mainIndexColumn = -1;
// } else {
// }
if (mainIndexColumn != -1) {
primaryIndex.setMainIndexColumn(mainIndexColumn);
index = new MVDelegateIndex(this, indexId,
indexName, primaryIndex, indexType);
} else {
index = new MVSecondaryIndex(session.getDatabase(),
this, indexId,
indexName, cols, indexType);
}
// } else {
// index = new TreeIndex(this, indexId, indexName, cols, indexType);
// }
if (index.needRebuild() && rowCount > 0) {
try {
Index scan = getScanIndex(session);
long remaining = scan.getRowCount(session);
long total = remaining;
Cursor cursor = scan.find(session, null, null);
long i = 0;
int bufferSize = (int) Math.min(rowCount, Constants.DEFAULT_MAX_MEMORY_ROWS);
ArrayList<Row> buffer = New.arrayList(bufferSize);
String n = getName() + ":" + index.getName();
int t = MathUtils.convertLongToInt(total);
while (cursor.next()) {
database.setProgress(DatabaseEventListener.STATE_CREATE_INDEX, n,
MathUtils.convertLongToInt(i++), t);
Row row = cursor.get();
buffer.add(row);
if (buffer.size() >= bufferSize) {
addRowsToIndex(session, buffer, index);
}
remaining--;
}
addRowsToIndex(session, buffer, index);
if (SysProperties.CHECK && remaining != 0) {
DbException.throwInternalError("rowcount remaining=" + remaining + " " + getName());
}
} catch (DbException e) {
getSchema().freeUniqueName(indexName);
try {
index.remove(session);
} catch (DbException e2) {
// this could happen, for example on failure in the storage
// but if that is not the case it means
// there is something wrong with the database
trace.error(e2, "could not remove index");
throw e2;
}
throw e;
}
}
index.setTemporary(isTemporary());
if (index.getCreateSQL() != null) {
index.setComment(indexComment);
if (isSessionTemporary) {
session.addLocalTempTableIndex(index);
} else {
database.addSchemaObject(session, index);
}
}
indexes.add(index);
setModified();
return index;
}
private int getMainIndexColumn(IndexType indexType, IndexColumn[] cols) {
if (primaryIndex.getMainIndexColumn() != -1) {
return -1;
}
if (!indexType.isPrimaryKey() || cols.length != 1) {
return -1;
}
IndexColumn first = cols[0];
if (first.sortType != SortOrder.ASCENDING) {
return -1;
}
switch(first.column.getType()) {
case Value.BYTE:
case Value.SHORT:
case Value.INT:
case Value.LONG:
break;
default:
return -1;
}
return first.column.getColumnId();
}
private void addRowsToIndex(Session session, ArrayList<Row> list, Index index) {
final Index idx = index;
Collections.sort(list, new Comparator<Row>() {
public int compare(Row r1, Row r2) {
return idx.compareRows(r1, r2);
}
});
for (Row row : list) {
index.add(session, row);
}
list.clear();
storeIfRequired();
}
@Override
public void removeRow(Session session, Row row) {
lastModificationId = database.getNextModificationDataId();
int i = indexes.size() - 1;
try {
for (; i >= 0; i--) {
Index index = indexes.get(i);
index.remove(session, row);
checkRowCount(session, index, -1);
}
rowCount--;
} catch (Throwable e) {
try {
while (++i < indexes.size()) {
Index index = indexes.get(i);
index.add(session, row);
checkRowCount(session, index, 0);
}
} catch (DbException e2) {
// this could happen, for example on failure in the storage
// but if that is not the case it means there is something wrong
// with the database
trace.error(e2, "could not undo operation");
throw e2;
}
throw DbException.convert(e);
}
analyzeIfRequired(session);
storeIfRequired();
}
@Override
public void truncate(Session session) {
lastModificationId = database.getNextModificationDataId();
for (int i = indexes.size() - 1; i >= 0; i--) {
Index index = indexes.get(i);
index.truncate(session);
}
rowCount = 0;
storeIfRequired();
}
@Override
public void addRow(Session session, Row row) {
lastModificationId = database.getNextModificationDataId();
int i = 0;
try {
for (int size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
index.add(session, row);
checkRowCount(session, index, 1);
}
rowCount++;
} catch (Throwable e) {
try {
while (--i >= 0) {
Index index = indexes.get(i);
index.remove(session, row);
checkRowCount(session, index, 0);
}
} catch (DbException e2) {
// this could happen, for example on failure in the storage
// but if that is not the case it means there is something wrong
// with the database
trace.error(e2, "could not undo operation");
throw e2;
}
DbException de = DbException.convert(e);
if (de.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
for (int j = 0; j < indexes.size(); j++) {
Index index = indexes.get(j);
if (index.getIndexType().isUnique() && index instanceof MultiVersionIndex) {
MultiVersionIndex mv = (MultiVersionIndex) index;
if (mv.isUncommittedFromOtherSession(session, row)) {
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, index.getName());
}
}
}
}
throw de;
}
analyzeIfRequired(session);
storeIfRequired();
}
private void checkRowCount(Session session, Index index, int offset) {
// TODO verify
}
private void analyzeIfRequired(Session session) {
// TODO analyze
}
@Override
public void checkSupportAlter() {
// ok
}
@Override
public String getTableType() {
return Table.EXTERNAL_TABLE_ENGINE;
}
@Override
public Index getScanIndex(Session session) {
return primaryIndex;
}
@Override
public Index getUniqueIndex() {
return primaryIndex;
}
@Override
public ArrayList<Index> getIndexes() {
return indexes;
}
@Override
public long getMaxDataModificationId() {
return lastModificationId;
}
@Override
public boolean isDeterministic() {
return true;
}
@Override
public boolean canGetRowCount() {
return true;
}
@Override
public boolean canDrop() {
return true;
}
public void removeChildrenAndResources(Session session) {
super.removeChildrenAndResources(session);
// go backwards because database.removeIndex will call table.removeIndex
while (indexes.size() > 1) {
Index index = indexes.get(1);
if (index.getName() != null) {
database.removeSchemaObject(session, index);
}
}
if (SysProperties.CHECK) {
for (SchemaObject obj : database.getAllSchemaObjects(DbObject.INDEX)) {
Index index = (Index) obj;
if (index.getTable() == this) {
DbException.throwInternalError("index not dropped: " + index.getName());
}
}
}
primaryIndex.remove(session);
database.removeMeta(session, getId());
primaryIndex = null;
close(session);
invalidate();
}
@Override
public long getRowCount(Session session) {
return primaryIndex.getRowCount(session);
}
@Override
public long getRowCountApproximation() {
return primaryIndex.getRowCountApproximation();
}
@Override
public void checkRename() {
// ok
}
private void storeIfRequired() {
if (store.getUnsavedPageCount() > 1000) {
store.store();
}
}
public MVStore getStore() {
return 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 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 java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.type.DataType;
import org.h2.result.SortOrder;
import org.h2.store.Data;
import org.h2.store.DataHandler;
import org.h2.store.LobStorage;
import org.h2.tools.SimpleResultSet;
import org.h2.util.DateTimeUtils;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueByte;
import org.h2.value.ValueBytes;
import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat;
import org.h2.value.ValueInt;
import org.h2.value.ValueJavaObject;
import org.h2.value.ValueLob;
import org.h2.value.ValueLobDb;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.h2.value.ValueResultSet;
import org.h2.value.ValueShort;
import org.h2.value.ValueString;
import org.h2.value.ValueStringFixed;
import org.h2.value.ValueStringIgnoreCase;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueUuid;
/**
* A row type.
*/
public class ValueArrayDataType implements DataType {
static final String PREFIX = ValueArrayDataType.class.getName();
private static final int INT_0_15 = 32;
private static final int LONG_0_7 = 48;
private static final int DECIMAL_0_1 = 56;
private static final int DECIMAL_SMALL_0 = 58;
private static final int DECIMAL_SMALL = 59;
private static final int DOUBLE_0_1 = 60;
private static final int FLOAT_0_1 = 62;
private static final int BOOLEAN_FALSE = 64;
private static final int BOOLEAN_TRUE = 65;
private static final int INT_NEG = 66;
private static final int LONG_NEG = 67;
private static final int STRING_0_31 = 68;
private static final int BYTES_0_31 = 100;
private static final int LOCAL_TIME = 132;
private static final int LOCAL_DATE = 133;
private static final int LOCAL_TIMESTAMP = 134;
private static final long MILLIS_PER_MINUTE = 1000 * 60;
final DataHandler handler;
final CompareMode compareMode;
final int[] sortTypes;
ValueArrayDataType(CompareMode compareMode, DataHandler handler, int[] sortTypes) {
this.compareMode = compareMode;
this.handler = handler;
this.sortTypes = sortTypes;
}
public int compare(Object a, Object b) {
if (a == b) {
return 0;
}
Value[] ax = (Value[]) a;
Value[] bx = (Value[]) b;
int al = ax.length;
int bl = bx.length;
int len = Math.min(al, bl);
for (int i = 0; i < len; i++) {
int comp = compareValues(ax[i], bx[i], sortTypes[i]);
if (comp != 0) {
return comp;
}
}
if (len < al) {
return -1;
} else if (len < bl) {
return 1;
}
return 0;
}
private int compareValues(Value a, Value b, int sortType) {
if (a == b) {
return 0;
}
boolean aNull = a == null, bNull = b == null;
if (aNull || bNull) {
return SortOrder.compareNull(aNull, sortType);
}
int comp = compareTypeSave(a, b);
if ((sortType & SortOrder.DESCENDING) != 0) {
comp = -comp;
}
return comp;
}
public int compareTypeSave(Value a, Value b) {
if (a == b) {
return 0;
}
int dataType = Value.getHigherOrder(a.getType(), b.getType());
a = a.convertTo(dataType);
b = b.convertTo(dataType);
return a.compareTypeSave(b, compareMode);
}
public int getMaxLength(Object obj) {
Value[] x = (Value[]) obj;
int len = x.length;
int result = DataUtils.MAX_VAR_INT_LEN;
for (int i = 0; i < len; i++) {
result += getMaxLength(x[i]);
}
return result;
}
private int getMaxLength(Value v) {
return Data.getValueLen(v, handler);
}
public int getMemory(Object obj) {
Value[] x = (Value[]) obj;
int len = x.length;
int memory = 0;
for (int i = 0; i < len; i++) {
memory += getMemory(x[i]);
}
return memory;
}
private static int getMemory(Value v) {
return v.getMemory();
}
public Value[] read(ByteBuffer buff) {
int len = DataUtils.readVarInt(buff);
Value[] x = new Value[len];
for (int i = 0; i < len; i++) {
x[i] = readValue(buff);
}
return x;
}
public void write(ByteBuffer buff, Object obj) {
Value[] x = (Value[]) obj;
int len = x.length;
DataUtils.writeVarInt(buff, len);
for (int i = 0; i < len; i++) {
writeValue(buff, x[i]);
}
}
public String asString() {
StringBuilder buff = new StringBuilder();
buff.append(PREFIX);
buff.append('(');
for (int i = 0; i < sortTypes.length; i++) {
if (i > 0) {
buff.append(',');
}
buff.append(sortTypes[i]);
}
buff.append(')');
return buff.toString();
}
/**
* Convert a row type to a row.
*
* @param t the type string
* @param factory the data type factory
* @return the row type
*/
static ValueArrayDataType fromString(CompareMode compareMode, DataHandler handler, String t) {
if (!t.startsWith(PREFIX) || !t.endsWith(")")) {
throw new RuntimeException("Unknown type: " + t);
}
t = t.substring(PREFIX.length(), t.length() - 1);
String[] array = StringUtils.arraySplit(t, ',', false);
int[] sortTypes = new int[array.length];
for (int i = 0; i < array.length; i++) {
sortTypes[i] = Integer.parseInt(array[i]);
}
return new ValueArrayDataType(compareMode, handler, sortTypes);
}
private void writeValue(ByteBuffer buff, Value v) {
int start = buff.position();
if (v == ValueNull.INSTANCE) {
buff.put((byte) 0);
return;
}
int type = v.getType();
switch (type) {
case Value.BOOLEAN:
buff.put((byte) (v.getBoolean().booleanValue() ? BOOLEAN_TRUE : BOOLEAN_FALSE));
break;
case Value.BYTE:
buff.put((byte) type);
buff.put(v.getByte());
break;
case Value.SHORT:
buff.put((byte) type);
buff.putShort(v.getShort());
break;
case Value.INT: {
int x = v.getInt();
if (x < 0) {
buff.put((byte) INT_NEG);
writeVarInt(buff, -x);
} else if (x < 16) {
buff.put((byte) (INT_0_15 + x));
} else {
buff.put((byte) type);
writeVarInt(buff, x);
}
break;
}
case Value.LONG: {
long x = v.getLong();
if (x < 0) {
buff.put((byte) LONG_NEG);
writeVarLong(buff, -x);
} else if (x < 8) {
buff.put((byte) (LONG_0_7 + x));
} else {
buff.put((byte) type);
writeVarLong(buff, x);
}
break;
}
case Value.DECIMAL: {
BigDecimal x = v.getBigDecimal();
if (BigDecimal.ZERO.equals(x)) {
buff.put((byte) DECIMAL_0_1);
} else if (BigDecimal.ONE.equals(x)) {
buff.put((byte) (DECIMAL_0_1 + 1));
} else {
int scale = x.scale();
BigInteger b = x.unscaledValue();
int bits = b.bitLength();
if (bits <= 63) {
if (scale == 0) {
buff.put((byte) DECIMAL_SMALL_0);
writeVarLong(buff, b.longValue());
} else {
buff.put((byte) DECIMAL_SMALL);
writeVarInt(buff, scale);
writeVarLong(buff, b.longValue());
}
} else {
buff.put((byte) type);
writeVarInt(buff, scale);
byte[] bytes = b.toByteArray();
writeVarInt(buff, bytes.length);
buff.put(bytes, 0, bytes.length);
}
}
break;
}
case Value.TIME:
if (SysProperties.STORE_LOCAL_TIME) {
buff.put((byte) LOCAL_TIME);
ValueTime t = (ValueTime) v;
long nanos = t.getNanos();
long millis = nanos / 1000000;
nanos -= millis * 1000000;
writeVarLong(buff, millis);
writeVarLong(buff, nanos);
} else {
buff.put((byte) type);
writeVarLong(buff, DateTimeUtils.getTimeLocalWithoutDst(v.getTime()));
}
break;
case Value.DATE: {
if (SysProperties.STORE_LOCAL_TIME) {
buff.put((byte) LOCAL_DATE);
long x = ((ValueDate) v).getDateValue();
writeVarLong(buff, x);
} else {
buff.put((byte) type);
long x = DateTimeUtils.getTimeLocalWithoutDst(v.getDate());
writeVarLong(buff, x / MILLIS_PER_MINUTE);
}
break;
}
case Value.TIMESTAMP: {
if (SysProperties.STORE_LOCAL_TIME) {
buff.put((byte) LOCAL_TIMESTAMP);
ValueTimestamp ts = (ValueTimestamp) v;
long dateValue = ts.getDateValue();
writeVarLong(buff, dateValue);
long nanos = ts.getNanos();
long millis = nanos / 1000000;
nanos -= millis * 1000000;
writeVarLong(buff, millis);
writeVarLong(buff, nanos);
} else {
Timestamp ts = v.getTimestamp();
buff.put((byte) type);
writeVarLong(buff, DateTimeUtils.getTimeLocalWithoutDst(ts));
writeVarInt(buff, ts.getNanos());
}
break;
}
case Value.JAVA_OBJECT: {
buff.put((byte) type);
byte[] b = v.getBytesNoCopy();
writeVarInt(buff, b.length);
buff.put(b, 0, b.length);
break;
}
case Value.BYTES: {
byte[] b = v.getBytesNoCopy();
int len = b.length;
if (len < 32) {
buff.put((byte) (BYTES_0_31 + len));
buff.put(b, 0, b.length);
} else {
buff.put((byte) type);
writeVarInt(buff, b.length);
buff.put(b, 0, b.length);
}
break;
}
case Value.UUID: {
buff.put((byte) type);
ValueUuid uuid = (ValueUuid) v;
buff.putLong(uuid.getHigh());
buff.putLong(uuid.getLow());
break;
}
case Value.STRING: {
String s = v.getString();
int len = s.length();
if (len < 32) {
buff.put((byte) (STRING_0_31 + len));
writeStringWithoutLength(buff, s, len);
} else {
buff.put((byte) type);
writeString(buff, s);
}
break;
}
case Value.STRING_IGNORECASE:
case Value.STRING_FIXED:
buff.put((byte) type);
writeString(buff, v.getString());
break;
case Value.DOUBLE: {
double x = v.getDouble();
if (x == 1.0d) {
buff.put((byte) (DOUBLE_0_1 + 1));
} else {
long d = Double.doubleToLongBits(x);
if (d == ValueDouble.ZERO_BITS) {
buff.put((byte) DOUBLE_0_1);
} else {
buff.put((byte) type);
writeVarLong(buff, Long.reverse(d));
}
}
break;
}
case Value.FLOAT: {
float x = v.getFloat();
if (x == 1.0f) {
buff.put((byte) (FLOAT_0_1 + 1));
} else {
int f = Float.floatToIntBits(x);
if (f == ValueFloat.ZERO_BITS) {
buff.put((byte) FLOAT_0_1);
} else {
buff.put((byte) type);
writeVarInt(buff, Integer.reverse(f));
}
}
break;
}
case Value.BLOB:
case Value.CLOB: {
buff.put((byte) type);
if (v instanceof ValueLob) {
ValueLob lob = (ValueLob) v;
lob.convertToFileIfRequired(handler);
byte[] small = lob.getSmall();
if (small == null) {
int t = -1;
if (!lob.isLinked()) {
t = -2;
}
writeVarInt(buff, t);
writeVarInt(buff, lob.getTableId());
writeVarInt(buff, lob.getObjectId());
writeVarLong(buff, lob.getPrecision());
buff.put((byte) (lob.useCompression() ? 1 : 0));
if (t == -2) {
writeString(buff, lob.getFileName());
}
} else {
writeVarInt(buff, small.length);
buff.put(small, 0, small.length);
}
} else {
ValueLobDb lob = (ValueLobDb) v;
byte[] small = lob.getSmall();
if (small == null) {
writeVarInt(buff, -3);
writeVarInt(buff, lob.getTableId());
writeVarLong(buff, lob.getLobId());
writeVarLong(buff, lob.getPrecision());
} else {
writeVarInt(buff, small.length);
buff.put(small, 0, small.length);
}
}
break;
}
case Value.ARRAY: {
buff.put((byte) type);
Value[] list = ((ValueArray) v).getList();
writeVarInt(buff, list.length);
for (Value x : list) {
writeValue(buff, x);
}
break;
}
case Value.RESULT_SET: {
buff.put((byte) type);
try {
ResultSet rs = ((ValueResultSet) v).getResultSet();
rs.beforeFirst();
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
writeVarInt(buff, columnCount);
for (int i = 0; i < columnCount; i++) {
writeString(buff, meta.getColumnName(i + 1));
writeVarInt(buff, meta.getColumnType(i + 1));
writeVarInt(buff, meta.getPrecision(i + 1));
writeVarInt(buff, meta.getScale(i + 1));
}
while (rs.next()) {
buff.put((byte) 1);
for (int i = 0; i < columnCount; i++) {
int t = org.h2.value.DataType.convertSQLTypeToValueType(meta.getColumnType(i + 1));
Value val = org.h2.value.DataType.readValue(null, rs, i + 1, t);
writeValue(buff, val);
}
}
buff.put((byte) 0);
rs.beforeFirst();
} catch (SQLException e) {
throw DbException.convert(e);
}
break;
}
default:
DbException.throwInternalError("type=" + v.getType());
}
if (SysProperties.CHECK2) {
if (buff.position() - start != Data.getValueLen(v, handler)) {
throw DbException
.throwInternalError("value size error: got " + (buff.position() - start) + " expected " + Data.getValueLen(v, handler));
}
}
}
private static void writeVarInt(ByteBuffer buff, int x) {
while ((x & ~0x7f) != 0) {
buff.put((byte) (0x80 | (x & 0x7f)));
x >>>= 7;
}
buff.put((byte) x);
}
private static void writeVarLong(ByteBuffer buff, long x) {
while ((x & ~0x7f) != 0) {
buff.put((byte) ((x & 0x7f) | 0x80));
x >>>= 7;
}
buff.put((byte) x);
}
private static void writeString(ByteBuffer buff, String s) {
int len = s.length();
writeVarInt(buff, len);
writeStringWithoutLength(buff, s, len);
}
private static void writeStringWithoutLength(ByteBuffer buff, String s, int len) {
for (int i = 0; i < len; i++) {
int c = s.charAt(i);
if (c < 0x80) {
buff.put((byte) c);
} else if (c >= 0x800) {
buff.put((byte) (0xe0 | (c >> 12)));
buff.put((byte) (((c >> 6) & 0x3f)));
buff.put((byte) (c & 0x3f));
} else {
buff.put((byte) (0xc0 | (c >> 6)));
buff.put((byte) (c & 0x3f));
}
}
}
// private void writeStringWithoutLength(char[] chars, int len) {
// int p = pos;
// byte[] buff = data;
// for (int i = 0; i < len; i++) {
// int c = chars[i];
// if (c < 0x80) {
// buff[p++] = (byte) c;
// } else if (c >= 0x800) {
// buff[p++] = (byte) (0xe0 | (c >> 12));
// buff[p++] = (byte) (((c >> 6) & 0x3f));
// buff[p++] = (byte) (c & 0x3f);
// } else {
// buff[p++] = (byte) (0xc0 | (c >> 6));
// buff[p++] = (byte) (c & 0x3f);
// }
// }
// pos = p;
// }
/**
* Read a value.
*
* @return the value
*/
private Value readValue(ByteBuffer buff) {
int type = buff.get() & 255;
switch (type) {
case Value.NULL:
return ValueNull.INSTANCE;
case BOOLEAN_TRUE:
return ValueBoolean.get(true);
case BOOLEAN_FALSE:
return ValueBoolean.get(false);
case INT_NEG:
return ValueInt.get(-readVarInt(buff));
case Value.INT:
return ValueInt.get(readVarInt(buff));
case LONG_NEG:
return ValueLong.get(-readVarLong(buff));
case Value.LONG:
return ValueLong.get(readVarLong(buff));
case Value.BYTE:
return ValueByte.get(buff.get());
case Value.SHORT:
return ValueShort.get(buff.getShort());
case DECIMAL_0_1:
return (ValueDecimal) ValueDecimal.ZERO;
case DECIMAL_0_1 + 1:
return (ValueDecimal) ValueDecimal.ONE;
case DECIMAL_SMALL_0:
return ValueDecimal.get(BigDecimal.valueOf(readVarLong(buff)));
case DECIMAL_SMALL: {
int scale = readVarInt(buff);
return ValueDecimal.get(BigDecimal.valueOf(readVarLong(buff), scale));
}
case Value.DECIMAL: {
int scale = readVarInt(buff);
int len = readVarInt(buff);
byte[] buff2 = Utils.newBytes(len);
buff.get(buff2, 0, len);
BigInteger b = new BigInteger(buff2);
return ValueDecimal.get(new BigDecimal(b, scale));
}
case LOCAL_DATE: {
return ValueDate.fromDateValue(readVarLong(buff));
}
case Value.DATE: {
long x = readVarLong(buff) * MILLIS_PER_MINUTE;
return ValueDate.get(new Date(DateTimeUtils.getTimeUTCWithoutDst(x)));
}
case LOCAL_TIME: {
long nanos = readVarLong(buff) * 1000000 + readVarLong(buff);
return ValueTime.fromNanos(nanos);
}
case Value.TIME:
// need to normalize the year, month and day
return ValueTime.get(new Time(DateTimeUtils.getTimeUTCWithoutDst(readVarLong(buff))));
case LOCAL_TIMESTAMP: {
long dateValue = readVarLong(buff);
long nanos = readVarLong(buff) * 1000000 + readVarLong(buff);
return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos);
}
case Value.TIMESTAMP: {
Timestamp ts = new Timestamp(DateTimeUtils.getTimeUTCWithoutDst(readVarLong(buff)));
ts.setNanos(readVarInt(buff));
return ValueTimestamp.get(ts);
}
case Value.BYTES: {
int len = readVarInt(buff);
byte[] b = Utils.newBytes(len);
buff.get(b, 0, len);
return ValueBytes.getNoCopy(b);
}
case Value.JAVA_OBJECT: {
int len = readVarInt(buff);
byte[] b = Utils.newBytes(len);
buff.get(b, 0, len);
return ValueJavaObject.getNoCopy(null, b);
}
case Value.UUID:
return ValueUuid.get(buff.getLong(), buff.getLong());
case Value.STRING:
return ValueString.get(readString(buff));
case Value.STRING_IGNORECASE:
return ValueStringIgnoreCase.get(readString(buff));
case Value.STRING_FIXED:
return ValueStringFixed.get(readString(buff));
case FLOAT_0_1:
return ValueFloat.get(0);
case FLOAT_0_1 + 1:
return ValueFloat.get(1);
case DOUBLE_0_1:
return ValueDouble.get(0);
case DOUBLE_0_1 + 1:
return ValueDouble.get(1);
case Value.DOUBLE:
return ValueDouble.get(Double.longBitsToDouble(Long.reverse(readVarLong(buff))));
case Value.FLOAT:
return ValueFloat.get(Float.intBitsToFloat(Integer.reverse(readVarInt(buff))));
case Value.BLOB:
case Value.CLOB: {
int smallLen = readVarInt(buff);
if (smallLen >= 0) {
byte[] small = Utils.newBytes(smallLen);
buff.get(small, 0, smallLen);
return LobStorage.createSmallLob(type, small);
} else if (smallLen == -3) {
int tableId = readVarInt(buff);
long lobId = readVarLong(buff);
long precision = readVarLong(buff);
LobStorage lobStorage = handler.getLobStorage();
ValueLobDb lob = ValueLobDb.create(type, lobStorage, tableId, lobId, null, precision);
return lob;
} else {
int tableId = readVarInt(buff);
int objectId = readVarInt(buff);
long precision = 0;
boolean compression = false;
// -1: regular
// -2: regular, but not linked (in this case: including file name)
if (smallLen == -1 || smallLen == -2) {
precision = readVarLong(buff);
compression = buff.get() == 1;
}
ValueLob lob = ValueLob.open(type, handler, tableId, objectId, precision, compression);
if (smallLen == -2) {
lob.setFileName(readString(buff), false);
}
return lob;
}
}
case Value.ARRAY: {
int len = readVarInt(buff);
Value[] list = new Value[len];
for (int i = 0; i < len; i++) {
list[i] = readValue(buff);
}
return ValueArray.get(list);
}
case Value.RESULT_SET: {
SimpleResultSet rs = new SimpleResultSet();
int columns = readVarInt(buff);
for (int i = 0; i < columns; i++) {
rs.addColumn(readString(buff), readVarInt(buff), readVarInt(buff), readVarInt(buff));
}
while (true) {
if (buff.get() == 0) {
break;
}
Object[] o = new Object[columns];
for (int i = 0; i < columns; i++) {
o[i] = readValue(buff).getObject();
}
rs.addRow(o);
}
return ValueResultSet.get(rs);
}
default:
if (type >= INT_0_15 && type < INT_0_15 + 16) {
return ValueInt.get(type - INT_0_15);
} else if (type >= LONG_0_7 && type < LONG_0_7 + 8) {
return ValueLong.get(type - LONG_0_7);
} else if (type >= BYTES_0_31 && type < BYTES_0_31 + 32) {
int len = type - BYTES_0_31;
byte[] b = Utils.newBytes(len);
buff.get(b, 0, len);
return ValueBytes.getNoCopy(b);
} else if (type >= STRING_0_31 && type < STRING_0_31 + 32) {
return ValueString.get(readString(buff, type - STRING_0_31));
}
throw DbException.get(ErrorCode.FILE_CORRUPTED_1, "type: " + type);
}
}
private static int readVarInt(ByteBuffer buff) {
return DataUtils.readVarInt(buff);
}
private static long readVarLong(ByteBuffer buff) {
return DataUtils.readVarLong(buff);
}
private static String readString(ByteBuffer buff, int len) {
return DataUtils.readString(buff, len);
}
private static String readString(ByteBuffer buff) {
int len = readVarInt(buff);
return DataUtils.readString(buff, len);
}
}
/*
* 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论