提交 7703ad26 authored 作者: Andrei Tokar's avatar Andrei Tokar

Modify uniqness check in MVSecondaryIndex to use upper-bounded cursor

Push upper bound check from MVStoreCursor down to underlying map cursor
Propagate database compatibility mode down to Value comparison operation
上级 00478174
...@@ -60,7 +60,7 @@ class MVPlainTempResult extends MVTempResult { ...@@ -60,7 +60,7 @@ class MVPlainTempResult extends MVTempResult {
*/ */
MVPlainTempResult(Database database, Expression[] expressions, int visibleColumnCount) { MVPlainTempResult(Database database, Expression[] expressions, int visibleColumnCount) {
super(database, expressions.length, visibleColumnCount); super(database, expressions.length, visibleColumnCount);
ValueDataType valueType = new ValueDataType(database.getCompareMode(), database, new int[columnCount]); ValueDataType valueType = new ValueDataType(database, new int[columnCount]);
Builder<Long, ValueArray> builder = new MVMap.Builder<Long, ValueArray>() Builder<Long, ValueArray> builder = new MVMap.Builder<Long, ValueArray>()
.valueType(valueType).singleWriter(); .valueType(valueType).singleWriter();
map = store.openMap("tmp", builder); map = store.openMap("tmp", builder);
......
...@@ -51,9 +51,8 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -51,9 +51,8 @@ public class MVPrimaryIndex extends BaseIndex {
for (int i = 0; i < columns.length; i++) { for (int i = 0; i < columns.length; i++) {
sortTypes[i] = SortOrder.ASCENDING; sortTypes[i] = SortOrder.ASCENDING;
} }
ValueDataType keyType = new ValueDataType(null, null, null); ValueDataType keyType = ValueDataType.INSTANCE;
ValueDataType valueType = new ValueDataType(db.getCompareMode(), db, ValueDataType valueType = new ValueDataType(db, sortTypes);
sortTypes);
mapName = "table." + getId(); mapName = "table." + getId();
Transaction t = mvTable.getTransactionBegin(); Transaction t = mvTable.getTransactionBegin();
dataMap = t.openMap(mapName, keyType, valueType); dataMap = t.openMap(mapName, keyType, valueType);
......
...@@ -62,9 +62,8 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -62,9 +62,8 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
sortTypes[i] = columns[i].sortType; sortTypes[i] = columns[i].sortType;
} }
sortTypes[keyColumns - 1] = SortOrder.ASCENDING; sortTypes[keyColumns - 1] = SortOrder.ASCENDING;
ValueDataType keyType = new ValueDataType( ValueDataType keyType = new ValueDataType(db, sortTypes);
db.getCompareMode(), db, sortTypes); ValueDataType valueType = ValueDataType.INSTANCE;
ValueDataType valueType = new ValueDataType(null, null, null);
Transaction t = mvTable.getTransactionBegin(); Transaction t = mvTable.getTransactionBegin();
dataMap = t.openMap(mapName, keyType, valueType); dataMap = t.openMap(mapName, keyType, valueType);
dataMap.map.setVolatile(!indexType.isPersistent()); dataMap.map.setVolatile(!indexType.isPersistent());
...@@ -128,25 +127,18 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -128,25 +127,18 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
for (String bufferName : bufferNames) { for (String bufferName : bufferNames) {
Iterator<ValueArray> iter = openMap(bufferName).keyIterator(null); Iterator<ValueArray> iter = openMap(bufferName).keyIterator(null);
if (iter.hasNext()) { if (iter.hasNext()) {
queue.add(new Source(iter)); queue.offer(new Source(iter));
} }
} }
try { try {
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
Source s = queue.remove(); Source s = queue.poll();
ValueArray rowData = s.next(); ValueArray rowData = s.next();
SearchRow row = convertToSearchRow(rowData);
if (indexType.isUnique()) { if (indexType.isUnique() && !mayHaveNullDuplicates(row)) {
Value[] array = rowData.getList(); checkUnique(dataMap, rowData, Long.MIN_VALUE);
// don't change the original value
array = array.clone();
array[keyColumns - 1] = ValueLong.MIN;
ValueArray unique = ValueArray.get(array);
SearchRow row = convertToSearchRow(rowData);
if (!mayHaveNullDuplicates(row)) {
requireUnique(row, dataMap, unique);
}
} }
dataMap.putCommitted(rowData, ValueNull.INSTANCE); dataMap.putCommitted(rowData, ValueNull.INSTANCE);
...@@ -169,9 +161,8 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -169,9 +161,8 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
sortTypes[i] = indexColumns[i].sortType; sortTypes[i] = indexColumns[i].sortType;
} }
sortTypes[keyColumns - 1] = SortOrder.ASCENDING; sortTypes[keyColumns - 1] = SortOrder.ASCENDING;
ValueDataType keyType = new ValueDataType( ValueDataType keyType = new ValueDataType(database, sortTypes);
database.getCompareMode(), database, sortTypes); ValueDataType valueType = ValueDataType.INSTANCE;
ValueDataType valueType = new ValueDataType(null, null, null);
MVMap.Builder<ValueArray, Value> builder = MVMap.Builder<ValueArray, Value> builder =
new MVMap.Builder<ValueArray, Value>() new MVMap.Builder<ValueArray, Value>()
.singleWriter() .singleWriter()
...@@ -195,50 +186,35 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -195,50 +186,35 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
public void add(Session session, Row row) { public void add(Session session, Row row) {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
ValueArray array = convertToKey(row); ValueArray array = convertToKey(row);
ValueArray unique = null; boolean checkRequired = indexType.isUnique() && !mayHaveNullDuplicates(row);
if (indexType.isUnique()) { if (checkRequired) {
// this will detect committed entries only checkUnique(map, array, Long.MIN_VALUE);
unique = convertToKey(row);
unique.getList()[keyColumns - 1] = ValueLong.MIN;
if (mayHaveNullDuplicates(row)) {
// No further unique checks required
unique = null;
} else {
requireUnique(row, map, unique);
}
} }
try { try {
map.put(array, ValueNull.INSTANCE); map.put(array, ValueNull.INSTANCE);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw mvTable.convertException(e); throw mvTable.convertException(e);
} }
if (unique != null) {
// This code expects that mayHaveDuplicates(row) == false if (checkRequired) {
Iterator<Value> it = map.keyIterator(unique, null, true); checkUnique(map, array, row.getKey());
while (it.hasNext()) {
ValueArray k = (ValueArray) it.next();
if (compareRows(row, convertToSearchRow(k)) != 0) {
break;
}
if (map.isSameTransaction(k)) {
continue;
}
if (map.get(k) != null) {
// committed
throw getDuplicateKeyException(k.toString());
}
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
}
} }
} }
private void requireUnique(SearchRow row, TransactionMap<Value, Value> map, ValueArray unique) { private void checkUnique(TransactionMap<Value, Value> map, ValueArray row, long newKey) {
Value key = map.ceilingKey(unique); Iterator<Value> it = map.keyIterator(convertToKey(row, Boolean.FALSE), convertToKey(row, Boolean.TRUE), true);
if (key != null) { while (it.hasNext()) {
ValueArray k = (ValueArray) key; ValueArray rowData = (ValueArray)it.next();
if (compareRows(row, convertToSearchRow(k)) == 0) { Value[] array = rowData.getList();
// committed Value rowKey = array[array.length - 1];
throw getDuplicateKeyException(k.toString()); long rowId = rowKey.getLong();
if (newKey != rowId) {
if (map.get(rowData) != null) {
// committed
throw getDuplicateKeyException(rowKey.toString());
}
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
} }
} }
} }
...@@ -285,58 +261,23 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -285,58 +261,23 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
} }
private Cursor find(Session session, SearchRow first, boolean bigger, SearchRow last) { private Cursor find(Session session, SearchRow first, boolean bigger, SearchRow last) {
ValueArray min = convertToKey(first); ValueArray min = convertToKey(convertToKey(first), bigger);
if (min != null) { ValueArray max = convertToKey(convertToKey(last), Boolean.TRUE);
min.getList()[keyColumns - 1] = ValueLong.MIN; TransactionMap<Value,Value> map = getMap(session);
return new MVStoreCursor(session, map.keyIterator(min, max, false));
}
private ValueArray convertToKey(ValueArray r, Boolean minmax) {
if (r == null) {
return null;
} }
TransactionMap<Value, Value> map = getMap(session);
if (bigger && min != null) { Value[] values = r.getList().clone();
// search for the next: first skip 1, then 2, 4, 8, until ValueArray row = ValueArray.get(values);
// we have a higher key; then skip 4, 2,... if (minmax != null) {
// (binary search), until 1 values[values.length - 1] = ValueLong.get(minmax ? Long.MAX_VALUE : Long.MIN_VALUE);
int offset = 1;
while (true) {
ValueArray v = (ValueArray) map.relativeKey(min, offset);
if (v != null) {
boolean foundHigher = false;
for (int i = 0; i < keyColumns - 1; i++) {
int idx = columnIds[i];
Value b = first.getValue(idx);
if (b == null) {
break;
}
Value a = v.getList()[i];
if (database.compare(a, b) > 0) {
foundHigher = true;
break;
}
}
if (!foundHigher) {
offset += offset;
min = v;
continue;
}
}
if (offset > 1) {
offset /= 2;
continue;
}
if (map.get(v) == null) {
min = (ValueArray) map.higherKey(min);
if (min == null) {
break;
}
continue;
}
min = v;
break;
}
if (min == null) {
return new MVStoreCursor(session,
Collections.<Value>emptyIterator(), null);
}
} }
return new MVStoreCursor(session, map.keyIterator(min), last); return row;
} }
private ValueArray convertToKey(SearchRow r) { private ValueArray convertToKey(SearchRow r) {
...@@ -349,7 +290,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -349,7 +290,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
int idx = c.getColumnId(); int idx = c.getColumnId();
Value v = r.getValue(idx); Value v = r.getValue(idx);
if (v != null) { if (v != null) {
array[i] = v.convertTo(c.getType(), -1, null, database.getMode(), c.getEnumerators()); array[i] = v.convertTo(c.getType(), -1, database.getMode(), null, c.getEnumerators());
} }
} }
array[keyColumns - 1] = ValueLong.get(r.getKey()); array[keyColumns - 1] = ValueLong.get(r.getKey());
...@@ -420,7 +361,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -420,7 +361,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
while (true) { while (true) {
if (key == null) { if (key == null) {
return new MVStoreCursor(session, return new MVStoreCursor(session,
Collections.<Value>emptyIterator(), null); Collections.<Value>emptyIterator());
} }
if (((ValueArray) key).getList()[0] != ValueNull.INSTANCE) { if (((ValueArray) key).getList()[0] != ValueNull.INSTANCE) {
break; break;
...@@ -428,7 +369,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -428,7 +369,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
key = first ? map.higherKey(key) : map.lowerKey(key); key = first ? map.higherKey(key) : map.lowerKey(key);
} }
MVStoreCursor cursor = new MVStoreCursor(session, MVStoreCursor cursor = new MVStoreCursor(session,
Collections.singletonList(key).iterator(), null); Collections.singletonList(key).iterator());
cursor.next(); cursor.next();
return cursor; return cursor;
} }
...@@ -499,23 +440,20 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -499,23 +440,20 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
private final Session session; private final Session session;
private final Iterator<Value> it; private final Iterator<Value> it;
private final SearchRow last; private ValueArray current;
private Value current;
private SearchRow searchRow;
private Row row; private Row row;
MVStoreCursor(Session session, Iterator<Value> it, SearchRow last) { MVStoreCursor(Session session, Iterator<Value> it) {
this.session = session; this.session = session;
this.it = it; this.it = it;
this.last = last;
} }
@Override @Override
public Row get() { public Row get() {
if (row == null) { if (row == null) {
SearchRow r = getSearchRow(); if (current != null) {
if (r != null) { Value[] values = current.getList();
row = mvTable.getRow(session, r.getKey()); row = mvTable.getRow(session, values[values.length - 1].getLong());
} }
} }
return row; return row;
...@@ -523,24 +461,12 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex { ...@@ -523,24 +461,12 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
@Override @Override
public SearchRow getSearchRow() { public SearchRow getSearchRow() {
if (searchRow == null) { return current == null ? null : convertToSearchRow(current);
if (current != null) {
searchRow = convertToSearchRow((ValueArray) current);
}
}
return searchRow;
} }
@Override @Override
public boolean next() { public boolean next() {
current = it.hasNext() ? it.next() : null; current = it.hasNext() ? (ValueArray)it.next() : null;
searchRow = null;
if (current != null) {
if (last != null && compareRows(getSearchRow(), last) > 0) {
searchRow = null;
current = null;
}
}
row = null; row = null;
return current != null; return current != null;
} }
......
...@@ -166,12 +166,12 @@ class MVSortedTempResult extends MVTempResult { ...@@ -166,12 +166,12 @@ class MVSortedTempResult extends MVTempResult {
indexes = null; indexes = null;
} }
this.indexes = indexes; this.indexes = indexes;
ValueDataType keyType = new ValueDataType(database.getCompareMode(), database, sortTypes); ValueDataType keyType = new ValueDataType(database, sortTypes);
Builder<ValueArray, Long> builder = new MVMap.Builder<ValueArray, Long>().keyType(keyType); Builder<ValueArray, Long> builder = new MVMap.Builder<ValueArray, Long>().keyType(keyType);
map = store.openMap("tmp", builder); map = store.openMap("tmp", builder);
if (distinct && length != visibleColumnCount || distinctIndexes != null) { if (distinct && length != visibleColumnCount || distinctIndexes != null) {
int count = distinctIndexes != null ? distinctIndexes.length : visibleColumnCount; int count = distinctIndexes != null ? distinctIndexes.length : visibleColumnCount;
ValueDataType distinctType = new ValueDataType(database.getCompareMode(), database, new int[count]); ValueDataType distinctType = new ValueDataType(database, new int[count]);
Builder<ValueArray, Boolean> indexBuilder = new MVMap.Builder<ValueArray, Boolean>().keyType(distinctType); Builder<ValueArray, Boolean> indexBuilder = new MVMap.Builder<ValueArray, Boolean>().keyType(distinctType);
index = store.openMap("idx", indexBuilder); index = store.openMap("idx", indexBuilder);
} }
......
...@@ -93,7 +93,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex { ...@@ -93,7 +93,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
checkIndexColumnTypes(columns); checkIndexColumnTypes(columns);
} }
String mapName = "index." + getId(); String mapName = "index." + getId();
ValueDataType vt = new ValueDataType(null, null, null); ValueDataType vt = new ValueDataType(db, null);
VersionedValue.Type valueType = new VersionedValue.Type(vt); VersionedValue.Type valueType = new VersionedValue.Type(vt);
MVRTreeMap.Builder<VersionedValue> mapBuilder = MVRTreeMap.Builder<VersionedValue> mapBuilder =
new MVRTreeMap.Builder<VersionedValue>(). new MVRTreeMap.Builder<VersionedValue>().
......
...@@ -160,9 +160,8 @@ public class MVTableEngine implements TableEngine { ...@@ -160,9 +160,8 @@ public class MVTableEngine implements TableEngine {
if (!db.getSettings().reuseSpace) { if (!db.getSettings().reuseSpace) {
store.setReuseSpace(false); store.setReuseSpace(false);
} }
this.transactionStore = new TransactionStore( this.transactionStore = new TransactionStore(store,
store, new ValueDataType(db, null), db.getLockTimeout());
new ValueDataType(db.getCompareMode(), db, null), db.getLockTimeout());
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw convertIllegalStateException(e); throw convertIllegalStateException(e);
} }
......
...@@ -13,6 +13,8 @@ import java.sql.ResultSetMetaData; ...@@ -13,6 +13,8 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Mode;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.DataUtils; import org.h2.mvstore.DataUtils;
import org.h2.mvstore.WriteBuffer; import org.h2.mvstore.WriteBuffer;
...@@ -33,6 +35,7 @@ import org.h2.value.ValueBytes; ...@@ -33,6 +35,7 @@ import org.h2.value.ValueBytes;
import org.h2.value.ValueDate; import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal; import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble; import org.h2.value.ValueDouble;
import org.h2.value.ValueEnum;
import org.h2.value.ValueFloat; import org.h2.value.ValueFloat;
import org.h2.value.ValueGeometry; import org.h2.value.ValueGeometry;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
...@@ -73,12 +76,20 @@ public class ValueDataType implements DataType { ...@@ -73,12 +76,20 @@ public class ValueDataType implements DataType {
final DataHandler handler; final DataHandler handler;
final CompareMode compareMode; final CompareMode compareMode;
protected final Mode mode;
final int[] sortTypes; final int[] sortTypes;
SpatialDataType spatialType; SpatialDataType spatialType;
public ValueDataType(CompareMode compareMode, DataHandler handler, public static ValueDataType INSTANCE = new ValueDataType(null, Mode.getRegular(), null, null);
public ValueDataType(Database database, int[] sortTypes) {
this(database.getCompareMode(), database.getMode(), database, sortTypes);
}
private ValueDataType(CompareMode compareMode, Mode mode, DataHandler handler,
int[] sortTypes) { int[] sortTypes) {
this.compareMode = compareMode; this.compareMode = compareMode;
this.mode = mode;
this.handler = handler; this.handler = handler;
this.sortTypes = sortTypes; this.sortTypes = sortTypes;
} }
...@@ -103,7 +114,13 @@ public class ValueDataType implements DataType { ...@@ -103,7 +114,13 @@ public class ValueDataType implements DataType {
int len = Math.min(al, bl); int len = Math.min(al, bl);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int sortType = sortTypes == null ? SortOrder.ASCENDING : sortTypes[i]; int sortType = sortTypes == null ? SortOrder.ASCENDING : sortTypes[i];
int comp = compareValues(ax[i], bx[i], sortType); Value one = ax[i];
Value two = bx[i];
if (one == null || two == null) {
return compareValues(ax[len - 1], bx[len - 1], SortOrder.ASCENDING);
}
int comp = compareValues(one, two, sortType);
if (comp != 0) { if (comp != 0) {
return comp; return comp;
} }
...@@ -136,7 +153,12 @@ public class ValueDataType implements DataType { ...@@ -136,7 +153,12 @@ public class ValueDataType implements DataType {
if (aNull || bNull) { if (aNull || bNull) {
return SortOrder.compareNull(aNull, sortType); return SortOrder.compareNull(aNull, sortType);
} }
int comp = a.compareTypeSafe(b, compareMode);
int t2 = Value.getHigherOrder(a.getType(), b.getType());
String[] enumerators = ValueEnum.getEnumeratorsForBinaryOperation(a, b);
int comp = a.convertTo(t2, -1, mode, null, enumerators)
.compareTypeSafe(b.convertTo(t2, -1, mode, null, enumerators), compareMode);
if ((sortType & SortOrder.DESCENDING) != 0) { if ((sortType & SortOrder.DESCENDING) != 0) {
comp = -comp; comp = -comp;
} }
......
...@@ -622,12 +622,8 @@ public class Recover extends Tool implements DataHandler { ...@@ -622,12 +622,8 @@ public class Recover extends Tool implements DataHandler {
} }
String tableId = mapName.substring("table.".length()); String tableId = mapName.substring("table.".length());
if (Integer.parseInt(tableId) == 0) { if (Integer.parseInt(tableId) == 0) {
ValueDataType keyType = new ValueDataType(
null, this, null);
ValueDataType valueType = new ValueDataType(
null, this, null);
TransactionMap<Value, Value> dataMap = store.begin().openMap( TransactionMap<Value, Value> dataMap = store.begin().openMap(
mapName, keyType, valueType); mapName, ValueDataType.INSTANCE, ValueDataType.INSTANCE);
Iterator<Value> dataIt = dataMap.keyIterator(null); Iterator<Value> dataIt = dataMap.keyIterator(null);
while (dataIt.hasNext()) { while (dataIt.hasNext()) {
Value rowId = dataIt.next(); Value rowId = dataIt.next();
...@@ -660,12 +656,8 @@ public class Recover extends Tool implements DataHandler { ...@@ -660,12 +656,8 @@ public class Recover extends Tool implements DataHandler {
if (Integer.parseInt(tableId) == 0) { if (Integer.parseInt(tableId) == 0) {
continue; continue;
} }
ValueDataType keyType = new ValueDataType(
null, this, null);
ValueDataType valueType = new ValueDataType(
null, this, null);
TransactionMap<Value, Value> dataMap = store.begin().openMap( TransactionMap<Value, Value> dataMap = store.begin().openMap(
mapName, keyType, valueType); mapName, ValueDataType.INSTANCE, ValueDataType.INSTANCE);
Iterator<Value> dataIt = dataMap.keyIterator(null); Iterator<Value> dataIt = dataMap.keyIterator(null);
boolean init = false; boolean init = false;
while (dataIt.hasNext()) { while (dataIt.hasNext()) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论