提交 c7b592e3 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add external storage of temporary results for MVStore mode

上级 760c36b8
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.message.DbException;
import org.h2.mvstore.Cursor;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVMap.Builder;
import org.h2.result.ResultExternal;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueLong;
/**
* Plain temporary result.
*/
class MVPlainTempResult extends MVTempResult {
private final MVMap<ValueLong, ValueArray> map;
private long counter;
private Cursor<ValueLong, ValueArray> cursor;
private MVPlainTempResult(MVPlainTempResult parent) {
super(parent);
this.map = parent.map;
}
MVPlainTempResult(Session session, Expression[] expressions) {
super(session);
Database db = session.getDatabase();
ValueDataType keyType = new ValueDataType(null, null, null);
ValueDataType valueType = new ValueDataType(db.getCompareMode(), db, new int[expressions.length]);
Builder<ValueLong, ValueArray> builder = new MVMap.Builder<ValueLong, ValueArray>().keyType(keyType)
.valueType(valueType);
map = store.openMap("tmp", builder);
}
@Override
public int addRow(Value[] values) {
map.put(ValueLong.get(counter++), ValueArray.get(values));
return ++rowCount;
}
@Override
public boolean contains(Value[] values) {
throw DbException.getUnsupportedException("contains()");
}
@Override
public synchronized ResultExternal createShallowCopy() {
if (parent != null) {
return parent.createShallowCopy();
}
if (closed) {
return null;
}
childCount++;
return new MVPlainTempResult(this);
}
@Override
public Value[] next() {
if (cursor == null) {
cursor = map.cursor(null);
}
if (!cursor.hasNext()) {
return null;
}
cursor.next();
return cursor.getValue().getList();
}
@Override
public int removeRow(Value[] values) {
throw DbException.getUnsupportedException("removeRow()");
}
@Override
public void reset() {
cursor = null;
}
}
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import java.util.BitSet;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.mvstore.Cursor;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVMap.Builder;
import org.h2.result.ResultExternal;
import org.h2.result.SortOrder;
import org.h2.value.Value;
import org.h2.value.ValueArray;
/**
* Sorted temporary result.
*/
class MVSortedTempResult extends MVTempResult {
private final boolean distinct;
private final int[] indexes;
private final MVMap<ValueArray, Long> map;
private Cursor<ValueArray, Long> cursor;
private Value[] current;
private long valueCount;
private MVSortedTempResult(MVSortedTempResult parent) {
super(parent);
this.distinct = parent.distinct;
this.indexes = parent.indexes;
this.map = parent.map;
this.rowCount = parent.rowCount;
}
/**
* Creates a new sorted temporary result.
*/
MVSortedTempResult(Session session, Expression[] expressions, boolean distinct, SortOrder sort) {
super(session);
this.distinct = distinct;
Database db = session.getDatabase();
int length = expressions.length;
int[] sortTypes = new int[length];
int[] indexes;
if (sort != null) {
indexes = new int[length];
int[] colIndex = sort.getQueryColumnIndexes();
int len = colIndex.length;
BitSet used = new BitSet();
for (int i = 0; i < len; i++) {
int idx = colIndex[i];
used.set(idx);
indexes[i] = idx;
sortTypes[i] = sort.getSortTypes()[i];
}
int idx = 0;
for (int i = len; i < length; i++) {
idx = used.nextClearBit(idx);
indexes[i] = idx;
idx++;
}
sameOrder: {
for (int i = 0; i < length; i++) {
if (indexes[i] != i) {
break sameOrder;
}
}
indexes = null;
}
} else {
indexes = null;
}
this.indexes = indexes;
ValueDataType keyType = new ValueDataType(db.getCompareMode(), db, sortTypes);
Builder<ValueArray, Long> builder = new MVMap.Builder<ValueArray, Long>().keyType(keyType);
map = store.openMap("tmp", builder);
}
@Override
public int addRow(Value[] values) {
ValueArray key = getKey(values);
if (distinct) {
if (map.putIfAbsent(key, 1L) == null) {
rowCount++;
}
} else {
Long old = map.putIfAbsent(key, 1L);
if (old != null) {
map.put(key, old + 1);
}
rowCount++;
}
return rowCount;
}
@Override
public boolean contains(Value[] values) {
return map.containsKey(getKey(values));
}
@Override
public synchronized ResultExternal createShallowCopy() {
if (parent != null) {
return parent.createShallowCopy();
}
if (closed) {
return null;
}
childCount++;
return new MVSortedTempResult(this);
}
private ValueArray getKey(Value[] values) {
if (indexes != null) {
Value[] r = new Value[indexes.length];
for (int i = 0; i < indexes.length; i++) {
r[indexes[i]] = values[i];
}
values = r;
}
return ValueArray.get(values);
}
private Value[] getValue(Value[] key) {
if (indexes != null) {
Value[] r = new Value[indexes.length];
for (int i = 0; i < indexes.length; i++) {
r[i] = key[indexes[i]];
}
key = r;
}
return key;
}
@Override
public Value[] next() {
if (cursor == null) {
cursor = map.cursor(null);
current = null;
valueCount = 0L;
}
if (--valueCount > 0) {
return current;
}
current = null;
if (!cursor.hasNext()) {
return null;
}
current = getValue(cursor.next().getList());
valueCount = cursor.getValue();
return current;
}
@Override
public int removeRow(Value[] values) {
ValueArray key = getKey(values);
if (distinct) {
if (map.remove(key) != null) {
rowCount--;
}
} else {
Long old = map.remove(key);
if (old != null) {
long l = old;
if (l > 1) {
map.put(key, l - 1);
}
rowCount--;
}
}
return rowCount;
}
@Override
public void reset() {
cursor = null;
current = null;
valueCount = 0L;
}
}
......@@ -75,12 +75,7 @@ public class MVTableEngine implements TableEngine {
}
if (key != null) {
encrypted = true;
char[] password = new char[key.length / 2];
for (int i = 0; i < password.length; i++) {
password[i] = (char) (((key[i + i] & 255) << 16) |
((key[i + i + 1]) & 255));
}
builder.encryptionKey(password);
builder.encryptionKey(decodePassword(key));
}
if (db.getSettings().compressData) {
builder.compress();
......@@ -101,6 +96,15 @@ public class MVTableEngine implements TableEngine {
return store;
}
static char[] decodePassword(byte[] key) {
char[] password = new char[key.length / 2];
for (int i = 0; i < password.length; i++) {
password[i] = (char) (((key[i + i] & 255) << 16) |
((key[i + i + 1]) & 255));
}
return password;
}
@Override
public TableBase createTable(CreateTableData data) {
Database db = data.session.getDatabase();
......
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore.db;
import java.io.IOException;
import java.lang.ref.Reference;
import java.util.ArrayList;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.message.DbException;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.MVStore.Builder;
import org.h2.result.ResultExternal;
import org.h2.result.SortOrder;
import org.h2.store.fs.FileUtils;
import org.h2.util.TempFileDeleter;
import org.h2.value.Value;
/**
* Temporary result.
*/
public abstract class MVTempResult implements ResultExternal {
/**
* Creates MVStore-based temporary result.
*
* @param session
* session
* @param expressions
* expressions
* @param distinct
* is output distinct
* @param sort
* sort order, or {@code null}
* @return temporary result
*/
public static ResultExternal of(Session session, Expression[] expressions, boolean distinct, SortOrder sort) {
return distinct || sort != null ? new MVSortedTempResult(session, expressions, distinct, sort)
: new MVPlainTempResult(session, expressions);
}
final MVStore store;
int rowCount;
final MVTempResult parent;
int childCount;
boolean closed;
private final TempFileDeleter tempFileDeleter;
private final String fileName;
private final Reference<?> fileRef;
MVTempResult(MVTempResult parent) {
this.parent = parent;
this.store = parent.store;
this.fileName = null;
this.tempFileDeleter = null;
this.fileRef = null;
}
MVTempResult(Session session) {
try {
fileName = FileUtils.createTempFile("h2tmp", Constants.SUFFIX_TEMP_FILE, false, true);
Builder builder = new MVStore.Builder().autoCommitDisabled().fileName(fileName);
byte[] key = session.getDatabase().getFileEncryptionKey();
if (key != null) {
builder.encryptionKey(MVTableEngine.decodePassword(key));
}
store = builder.open();
tempFileDeleter = session.getDatabase().getTempFileDeleter();
fileRef = tempFileDeleter.addFile(fileName, this);
} catch (IOException e) {
throw DbException.convert(e);
}
parent = null;
}
@Override
public int addRows(ArrayList<Value[]> rows) {
for (Value[] row : rows) {
addRow(row);
}
return rowCount;
}
@Override
public synchronized void close() {
if (closed) {
return;
}
closed = true;
if (parent != null) {
parent.closeChild();
} else {
if (childCount == 0) {
delete();
}
}
}
private synchronized void closeChild() {
if (--childCount == 0 && closed) {
delete();
}
}
private void delete() {
store.closeImmediately();
tempFileDeleter.deleteFile(fileRef, fileName);
}
@Override
public void done() {
// Do nothing
}
}
......@@ -15,6 +15,7 @@ import org.h2.engine.Session;
import org.h2.engine.SessionInterface;
import org.h2.expression.Expression;
import org.h2.message.DbException;
import org.h2.mvstore.db.MVTempResult;
import org.h2.util.Utils;
import org.h2.util.ValueHashMap;
import org.h2.value.DataType;
......@@ -290,7 +291,9 @@ public class LocalResult implements ResultInterface, ResultTarget {
}
private void createExternalResult() {
external = new ResultTempTable(session, expressions, distinct, sort);
external = session.getDatabase().getMvStore() != null
? MVTempResult.of(session, expressions, distinct, sort)
: new ResultTempTable(session, expressions, distinct, sort);
}
/**
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论