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

Add javadoc and some assertions

上级 7f7480d7
...@@ -22,20 +22,54 @@ import org.h2.value.ValueLong; ...@@ -22,20 +22,54 @@ import org.h2.value.ValueLong;
*/ */
class MVPlainTempResult extends MVTempResult { class MVPlainTempResult extends MVTempResult {
/**
* The type of the values in the main map and keys in the index.
*/
private final ValueDataType valueType; private final ValueDataType valueType;
/**
* Map with identities of rows as keys rows as values.
*/
private final MVMap<ValueLong, ValueArray> map; private final MVMap<ValueLong, ValueArray> map;
/**
* Counter for the identities of rows. A separate counter is used instead of
* {@link #rowCount} because rows due to presence of {@link #removeRow(Value[])}
* method to ensure that each row will have an own identity.
*/
private long counter; private long counter;
/**
* Optional index. This index is created only if {@link #contains(Value[])}
* method is invoked. Only the root result should have an index if required.
*/
private MVMap<ValueArray, Boolean> index; private MVMap<ValueArray, Boolean> index;
/**
* Cursor for the {@link #next()} method.
*/
private Cursor<ValueLong, ValueArray> cursor; private Cursor<ValueLong, ValueArray> cursor;
/**
* Creates a shallow copy of the result.
*
* @param parent
* parent result
*/
private MVPlainTempResult(MVPlainTempResult parent) { private MVPlainTempResult(MVPlainTempResult parent) {
super(parent); super(parent);
this.valueType = null; this.valueType = null;
this.map = parent.map; this.map = parent.map;
} }
/**
* Creates a new plain temporary result.
*
* @param session
* database session.
* @param expressions
* column expressions
*/
MVPlainTempResult(Session session, Expression[] expressions) { MVPlainTempResult(Session session, Expression[] expressions) {
super(session); super(session);
Database db = session.getDatabase(); Database db = session.getDatabase();
...@@ -48,12 +82,14 @@ class MVPlainTempResult extends MVTempResult { ...@@ -48,12 +82,14 @@ class MVPlainTempResult extends MVTempResult {
@Override @Override
public int addRow(Value[] values) { public int addRow(Value[] values) {
assert parent == null && index == null;
map.put(ValueLong.get(counter++), ValueArray.get(values)); map.put(ValueLong.get(counter++), ValueArray.get(values));
return ++rowCount; return ++rowCount;
} }
@Override @Override
public boolean contains(Value[] values) { public boolean contains(Value[] values) {
// Only parent result maintains the index
if (parent != null) { if (parent != null) {
return parent.contains(values); return parent.contains(values);
} }
......
...@@ -20,17 +20,53 @@ import org.h2.value.ValueArray; ...@@ -20,17 +20,53 @@ import org.h2.value.ValueArray;
/** /**
* Sorted temporary result. * Sorted temporary result.
*
* <p>
* This result is used for distinct and/or sorted results.
* </p>
*/ */
class MVSortedTempResult extends MVTempResult { class MVSortedTempResult extends MVTempResult {
/**
* Whether this result is distinct.
*/
private final boolean distinct; private final boolean distinct;
/**
* Mapping of indexes of columns to its positions in the store, or {@code null}
* if columns are not reordered.
*/
private final int[] indexes; private final int[] indexes;
/**
* Map with rows as keys and counts of duplicate rows as values. If this map is
* distinct all values are 1.
*/
private final MVMap<ValueArray, Long> map; private final MVMap<ValueArray, Long> map;
/**
* Cursor for the {@link #next()} method.
*/
private Cursor<ValueArray, Long> cursor; private Cursor<ValueArray, Long> cursor;
/**
* Current value for the {@link #next()} method. Used in non-distinct results
* with duplicate rows.
*/
private Value[] current; private Value[] current;
/**
* Count of remaining duplicate rows for the {@link #next()} method. Used in
* non-distinct results.
*/
private long valueCount; private long valueCount;
/**
* Creates a shallow copy of the result.
*
* @param parent
* parent result
*/
private MVSortedTempResult(MVSortedTempResult parent) { private MVSortedTempResult(MVSortedTempResult parent) {
super(parent); super(parent);
this.distinct = parent.distinct; this.distinct = parent.distinct;
...@@ -41,6 +77,16 @@ class MVSortedTempResult extends MVTempResult { ...@@ -41,6 +77,16 @@ class MVSortedTempResult extends MVTempResult {
/** /**
* Creates a new sorted temporary result. * Creates a new sorted temporary result.
*
* @param session
* database session
* @param expressions
* column expressions
* @param distinct
* whether this result should be distinct
* @param sort
* sort order, or {@code null} if this result does not
* need any sorting
*/ */
MVSortedTempResult(Session session, Expression[] expressions, boolean distinct, SortOrder sort) { MVSortedTempResult(Session session, Expression[] expressions, boolean distinct, SortOrder sort) {
super(session); super(session);
...@@ -50,31 +96,53 @@ class MVSortedTempResult extends MVTempResult { ...@@ -50,31 +96,53 @@ class MVSortedTempResult extends MVTempResult {
int[] sortTypes = new int[length]; int[] sortTypes = new int[length];
int[] indexes; int[] indexes;
if (sort != null) { if (sort != null) {
/*
* If sorting is specified we need to reorder columns in requested order and set
* sort types (ASC, DESC etc) for them properly.
*/
indexes = new int[length]; indexes = new int[length];
int[] colIndex = sort.getQueryColumnIndexes(); int[] colIndex = sort.getQueryColumnIndexes();
int len = colIndex.length; int len = colIndex.length;
// This set is used to remember columns that are already included
BitSet used = new BitSet(); BitSet used = new BitSet();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int idx = colIndex[i]; int idx = colIndex[i];
assert !used.get(idx);
used.set(idx); used.set(idx);
indexes[i] = idx; indexes[i] = idx;
sortTypes[i] = sort.getSortTypes()[i]; sortTypes[i] = sort.getSortTypes()[i];
} }
/*
* Because this result may have more columns than specified in sorting we need
* to add all remaining columns to the mapping of columns. A default sorting
* order (ASC / 0) will be used for them.
*/
int idx = 0; int idx = 0;
for (int i = len; i < length; i++) { for (int i = len; i < length; i++) {
idx = used.nextClearBit(idx); idx = used.nextClearBit(idx);
indexes[i] = idx; indexes[i] = idx;
idx++; idx++;
} }
/*
* Sometimes columns may be not reordered. Because reordering of columns
* slightly slows down other methods we check whether columns are really
* reordered or have the same order.
*/
sameOrder: { sameOrder: {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
if (indexes[i] != i) { if (indexes[i] != i) {
// Columns are reordered
break sameOrder; break sameOrder;
} }
} }
/*
* Columns are not reordered, set this field to null to disable reordering in
* other methods.
*/
indexes = null; indexes = null;
} }
} else { } else {
// Columns are not reordered if sort order is not specified
indexes = null; indexes = null;
} }
this.indexes = indexes; this.indexes = indexes;
...@@ -85,14 +153,18 @@ class MVSortedTempResult extends MVTempResult { ...@@ -85,14 +153,18 @@ class MVSortedTempResult extends MVTempResult {
@Override @Override
public int addRow(Value[] values) { public int addRow(Value[] values) {
assert parent == null;
ValueArray key = getKey(values); ValueArray key = getKey(values);
if (distinct) { if (distinct) {
// Add a row and increment the counter only if row does not exist
if (map.putIfAbsent(key, 1L) == null) { if (map.putIfAbsent(key, 1L) == null) {
rowCount++; rowCount++;
} }
} else { } else {
// Try to set counter to 1 first if such row does not exist yet
Long old = map.putIfAbsent(key, 1L); Long old = map.putIfAbsent(key, 1L);
if (old != null) { if (old != null) {
// This rows is already in the map, increment its own counter
map.put(key, old + 1); map.put(key, old + 1);
} }
rowCount++; rowCount++;
...@@ -117,6 +189,13 @@ class MVSortedTempResult extends MVTempResult { ...@@ -117,6 +189,13 @@ class MVSortedTempResult extends MVTempResult {
return new MVSortedTempResult(this); return new MVSortedTempResult(this);
} }
/**
* Reorder values if required and convert them into {@link ValueArray}.
*
* @param values
* values
* @return ValueArray for maps
*/
private ValueArray getKey(Value[] values) { private ValueArray getKey(Value[] values) {
if (indexes != null) { if (indexes != null) {
Value[] r = new Value[indexes.length]; Value[] r = new Value[indexes.length];
...@@ -128,6 +207,13 @@ class MVSortedTempResult extends MVTempResult { ...@@ -128,6 +207,13 @@ class MVSortedTempResult extends MVTempResult {
return ValueArray.get(values); return ValueArray.get(values);
} }
/**
* Reorder values back if required.
*
* @param key
* reordered values
* @return original values
*/
private Value[] getValue(Value[] key) { private Value[] getValue(Value[] key) {
if (indexes != null) { if (indexes != null) {
Value[] r = new Value[indexes.length]; Value[] r = new Value[indexes.length];
...@@ -146,22 +232,35 @@ class MVSortedTempResult extends MVTempResult { ...@@ -146,22 +232,35 @@ class MVSortedTempResult extends MVTempResult {
current = null; current = null;
valueCount = 0L; valueCount = 0L;
} }
// If we have multiple rows with the same values return them all
if (--valueCount > 0) { if (--valueCount > 0) {
/*
* Underflow in valueCount is hypothetically possible after a lot of invocations
* (not really possible in practice), but current will be null anyway.
*/
return current; return current;
} }
current = null;
if (!cursor.hasNext()) { if (!cursor.hasNext()) {
// Set current to null to be sure
current = null;
return null; return null;
} }
// Read the next row
current = getValue(cursor.next().getList()); current = getValue(cursor.next().getList());
/*
* If valueCount is greater than 1 that is possible for non-distinct results the
* following invocations of next() will use this.current and this.valueCount.
*/
valueCount = cursor.getValue(); valueCount = cursor.getValue();
return current; return current;
} }
@Override @Override
public int removeRow(Value[] values) { public int removeRow(Value[] values) {
assert parent == null;
ValueArray key = getKey(values); ValueArray key = getKey(values);
if (distinct) { if (distinct) {
// If an entry was removed decrement the counter
if (map.remove(key) != null) { if (map.remove(key) != null) {
rowCount--; rowCount--;
} }
...@@ -170,6 +269,10 @@ class MVSortedTempResult extends MVTempResult { ...@@ -170,6 +269,10 @@ class MVSortedTempResult extends MVTempResult {
if (old != null) { if (old != null) {
long l = old; long l = old;
if (l > 1) { if (l > 1) {
/*
* We have more than one such row. Decrement its counter by 1 and put this row
* back into map.
*/
map.put(key, l - 1); map.put(key, l - 1);
} }
rowCount--; rowCount--;
......
...@@ -23,6 +23,13 @@ import org.h2.value.Value; ...@@ -23,6 +23,13 @@ import org.h2.value.Value;
/** /**
* Temporary result. * Temporary result.
*
* <p>
* A separate MVStore in a temporary file is used for each result. The file is
* removed when this result and all its copies are closed.
* {@link TempFileDeleter} is also used to delete this file if results are not
* closed properly.
* </p>
*/ */
public abstract class MVTempResult implements ResultExternal { public abstract class MVTempResult implements ResultExternal {
...@@ -44,20 +51,52 @@ public abstract class MVTempResult implements ResultExternal { ...@@ -44,20 +51,52 @@ public abstract class MVTempResult implements ResultExternal {
: new MVPlainTempResult(session, expressions); : new MVPlainTempResult(session, expressions);
} }
/**
* MVStore.
*/
final MVStore store; final MVStore store;
/**
* Count of rows. Used only in a root results, copies always have 0 value.
*/
int rowCount; int rowCount;
/**
* Parent store for copies. If {@code null} this result is a root result.
*/
final MVTempResult parent; final MVTempResult parent;
/**
* Count of child results.
*/
int childCount; int childCount;
/**
* Whether this result is closed.
*/
boolean closed; boolean closed;
/**
* Temporary file deleter.
*/
private final TempFileDeleter tempFileDeleter; private final TempFileDeleter tempFileDeleter;
/**
* Name of the temporary file.
*/
private final String fileName; private final String fileName;
/**
* Reference to the record in the temporary file deleter.
*/
private final Reference<?> fileRef; private final Reference<?> fileRef;
/**
* Creates a shallow copy of the result.
*
* @param parent
* parent result
*/
MVTempResult(MVTempResult parent) { MVTempResult(MVTempResult parent) {
this.parent = parent; this.parent = parent;
this.store = parent.store; this.store = parent.store;
...@@ -66,6 +105,12 @@ public abstract class MVTempResult implements ResultExternal { ...@@ -66,6 +105,12 @@ public abstract class MVTempResult implements ResultExternal {
this.fileRef = null; this.fileRef = null;
} }
/**
* Creates a new temporary result.
*
* @param session
* database session
*/
MVTempResult(Session session) { MVTempResult(Session session) {
try { try {
fileName = FileUtils.createTempFile("h2tmp", Constants.SUFFIX_TEMP_FILE, false, true); fileName = FileUtils.createTempFile("h2tmp", Constants.SUFFIX_TEMP_FILE, false, true);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论