Unverified 提交 c3afed94 authored 作者: Andrei Tokar's avatar Andrei Tokar 提交者: GitHub

Merge pull request #1622 from h2database/vv-null

Reuse VersionedValue value for NULL, encapsulate "value" field
......@@ -34,7 +34,7 @@ import org.h2.mvstore.db.MVTable;
import org.h2.mvstore.db.MVTableEngine;
import org.h2.mvstore.tx.Transaction;
import org.h2.mvstore.tx.TransactionStore;
import org.h2.mvstore.tx.VersionedValue;
import org.h2.value.VersionedValue;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.result.SortOrder;
......@@ -1830,7 +1830,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
private static Row getRowFromVersionedValue(MVTable table, long recKey,
VersionedValue versionedValue) {
Object value = versionedValue == null ? null : versionedValue.value;
Object value = versionedValue == null ? null : versionedValue.getCurrentValue();
if (value == null) {
return null;
}
......
......@@ -28,7 +28,8 @@ import org.h2.mvstore.rtree.MVRTreeMap.RTreeCursor;
import org.h2.mvstore.rtree.SpatialKey;
import org.h2.mvstore.tx.Transaction;
import org.h2.mvstore.tx.TransactionMap;
import org.h2.mvstore.tx.VersionedValue;
import org.h2.mvstore.tx.VersionedValueType;
import org.h2.value.VersionedValue;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
......@@ -98,7 +99,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
}
String mapName = "index." + getId();
ValueDataType vt = new ValueDataType(db, null);
VersionedValue.Type valueType = new VersionedValue.Type(vt);
VersionedValueType valueType = new VersionedValueType(vt);
MVRTreeMap.Builder<VersionedValue> mapBuilder =
new MVRTreeMap.Builder<VersionedValue>().
valueType(valueType);
......
......@@ -6,6 +6,7 @@
package org.h2.mvstore.tx;
import org.h2.mvstore.MVMap;
import org.h2.value.VersionedValue;
/**
* Class CommitDecisionMaker makes a decision during post-commit processing
......@@ -36,7 +37,7 @@ final class CommitDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
// see TxDecisionMaker.decide()
decision = MVMap.Decision.ABORT;
} else /* this is final undo log entry for this key */ if (existingValue.value == null) {
} else /* this is final undo log entry for this key */ if (existingValue.getCurrentValue() == null) {
decision = MVMap.Decision.REMOVE;
} else {
decision = MVMap.Decision.PUT;
......@@ -49,7 +50,7 @@ final class CommitDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
public VersionedValue selectValue(VersionedValue existingValue, VersionedValue providedValue) {
assert decision == MVMap.Decision.PUT;
assert existingValue != null;
return VersionedValue.getInstance(existingValue.value);
return VersionedValueCommitted.getInstance(existingValue.getCurrentValue());
}
@Override
......
......@@ -6,6 +6,7 @@
package org.h2.mvstore.tx;
import org.h2.mvstore.MVMap;
import org.h2.value.VersionedValue;
/**
* Class RollbackDecisionMaker process undo log record during transaction rollback.
......
......@@ -9,6 +9,7 @@ import org.h2.mvstore.DataUtils;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.type.DataType;
import org.h2.value.VersionedValue;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicLong;
......
......@@ -10,6 +10,7 @@ import org.h2.mvstore.DataUtils;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.Page;
import org.h2.mvstore.type.DataType;
import org.h2.value.VersionedValue;
import java.util.AbstractMap;
import java.util.AbstractSet;
......@@ -146,7 +147,7 @@ public class TransactionMap<K, V> extends AbstractMap<K, V> {
int txId = TransactionStore.getTransactionId(operationId);
boolean isVisible = txId == transaction.transactionId ||
committingTransactions.get(txId);
Object v = isVisible ? currentValue.value : currentValue.getCommittedValue();
Object v = isVisible ? currentValue.getCurrentValue() : currentValue.getCommittedValue();
if (v == null) {
--size;
}
......@@ -177,7 +178,7 @@ public class TransactionMap<K, V> extends AbstractMap<K, V> {
int txId = TransactionStore.getTransactionId(operationId);
boolean isVisible = txId == transaction.transactionId ||
committingTransactions.get(txId);
Object v = isVisible ? currentValue.value : currentValue.getCommittedValue();
Object v = isVisible ? currentValue.getCurrentValue() : currentValue.getCommittedValue();
if (v == null) {
--size;
}
......@@ -264,10 +265,10 @@ public class TransactionMap<K, V> extends AbstractMap<K, V> {
*/
public V putCommitted(K key, V value) {
DataUtils.checkArgument(value != null, "The value may not be null");
VersionedValue newValue = VersionedValue.getInstance(value);
VersionedValue newValue = VersionedValueCommitted.getInstance(value);
VersionedValue oldValue = map.put(key, newValue);
@SuppressWarnings("unchecked")
V result = (V) (oldValue == null ? null : oldValue.value);
V result = (V) (oldValue == null ? null : oldValue.getCurrentValue());
return result;
}
......@@ -300,7 +301,7 @@ public class TransactionMap<K, V> extends AbstractMap<K, V> {
transaction.blockingMap = null;
transaction.blockingKey = null;
@SuppressWarnings("unchecked")
V res = result == null ? null : (V) result.value;
V res = result == null ? null : (V) result.getCurrentValue();
return res;
}
decisionMaker.reset();
......@@ -383,12 +384,12 @@ public class TransactionMap<K, V> extends AbstractMap<K, V> {
long id = data.getOperationId();
if (id == 0) {
// it is committed
return (V)data.value;
return (V)data.getCurrentValue();
}
int tx = TransactionStore.getTransactionId(id);
if (tx == transaction.transactionId || transaction.store.committingTransactions.get().get(tx)) {
// added by this transaction or another transaction which is committed by now
return (V)data.value;
return (V) data.getCurrentValue();
} else {
return (V) data.getCommittedValue();
}
......@@ -657,7 +658,7 @@ public class TransactionMap<K, V> extends AbstractMap<K, V> {
@Override
@SuppressWarnings("unchecked")
protected Map.Entry<K, V> registerCurrent(K key, VersionedValue data) {
return new AbstractMap.SimpleImmutableEntry<>(key, (V) data.value);
return new AbstractMap.SimpleImmutableEntry<>(key, (V) data.getCurrentValue());
}
}
......@@ -712,12 +713,12 @@ public class TransactionMap<K, V> extends AbstractMap<K, V> {
// current value comes from another uncommitted transaction
// take committed value instead
Object committedValue = data.getCommittedValue();
data = committedValue == null ? null : VersionedValue.getInstance(committedValue);
data = committedValue == null ? null : VersionedValueCommitted.getInstance(committedValue);
}
}
}
}
if (data != null && (data.value != null ||
if (data != null && (data.getCurrentValue() != null ||
includeAllUncommitted && transactionId !=
TransactionStore.getTransactionId(data.getOperationId()))) {
current = registerCurrent(key, data);
......
......@@ -20,6 +20,7 @@ import org.h2.mvstore.WriteBuffer;
import org.h2.mvstore.type.DataType;
import org.h2.mvstore.type.ObjectDataType;
import org.h2.util.StringUtils;
import org.h2.value.VersionedValue;
/**
* A store that supports concurrent MVCC read-committed transactions.
......@@ -128,14 +129,14 @@ public class TransactionStore {
this.timeoutMillis = timeoutMillis;
preparedTransactions = store.openMap("openTransactions",
new MVMap.Builder<Integer, Object[]>());
DataType oldValueType = new VersionedValue.Type(dataType);
DataType oldValueType = new VersionedValueType(dataType);
ArrayType undoLogValueType = new ArrayType(new DataType[]{
new ObjectDataType(), dataType, oldValueType
});
undoLogBuilder = new MVMap.Builder<Long, Object[]>()
.singleWriter()
.valueType(undoLogValueType);
DataType vt = new VersionedValue.Type(dataType);
DataType vt = new VersionedValueType(dataType);
mapBuilder = new MVMap.Builder<Object, VersionedValue>()
.keyType(dataType).valueType(vt);
}
......@@ -494,7 +495,7 @@ public class TransactionStore {
if (valueType == null) {
valueType = new ObjectDataType();
}
VersionedValue.Type vt = new VersionedValue.Type(valueType);
VersionedValueType vt = new VersionedValueType(valueType);
MVMap<K, VersionedValue> map;
MVMap.Builder<K, VersionedValue> builder =
new MVMap.Builder<K, VersionedValue>().
......@@ -640,7 +641,7 @@ public class TransactionStore {
MVMap<Object, VersionedValue> m = openMap(mapId);
if (m != null) { // could be null if map was removed later on
VersionedValue oldValue = (VersionedValue) op[2];
current = new Change(m.getName(), op[1], oldValue == null ? null : oldValue.value);
current = new Change(m.getName(), op[1], oldValue == null ? null : oldValue.getCurrentValue());
return;
}
}
......
......@@ -6,6 +6,7 @@
package org.h2.mvstore.tx;
import org.h2.mvstore.MVMap;
import org.h2.value.VersionedValue;
/**
* Class TxDecisionMaker.
......@@ -47,7 +48,7 @@ abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
// We assume that we are looking at the final value for this transaction,
// and if it's not the case, then it will fail later,
// because a tree root has definitely been changed.
logIt(existingValue.value == null ? null : VersionedValue.getInstance(existingValue.value));
logIt(existingValue.getCurrentValue() == null ? null : VersionedValueCommitted.getInstance(existingValue.getCurrentValue()));
decision = MVMap.Decision.PUT;
} else if (getBlockingTransaction() != null) {
// this entry comes from a different transaction, and this
......@@ -63,7 +64,7 @@ abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
// was written but not undo log), and will effectively roll it back
// (just assume committed value and overwrite).
Object committedValue = existingValue.getCommittedValue();
logIt(committedValue == null ? null : VersionedValue.getInstance(committedValue));
logIt(committedValue == null ? null : VersionedValueCommitted.getInstance(committedValue));
decision = MVMap.Decision.PUT;
} else {
// transaction has been committed/rolled back and is closed by now, so
......@@ -138,7 +139,7 @@ abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
@SuppressWarnings("unchecked")
@Override
public final VersionedValue selectValue(VersionedValue existingValue, VersionedValue providedValue) {
return VersionedValue.getInstance(undoKey, value,
return VersionedValueUncommitted.getInstance(undoKey, value,
existingValue == null ? null : existingValue.getCommittedValue());
}
}
......@@ -163,7 +164,7 @@ abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
if (id == 0 // entry is a committed one
// or it came from the same transaction
|| isThisTransaction(blockingId = TransactionStore.getTransactionId(id))) {
if(existingValue.value != null) {
if(existingValue.getCurrentValue() != null) {
return setDecision(MVMap.Decision.ABORT);
}
logIt(existingValue);
......@@ -171,7 +172,7 @@ abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
} else if (isCommitted(blockingId)) {
// entry belongs to a committing transaction
// and therefore will be committed soon
if(existingValue.value != null) {
if(existingValue.getCurrentValue() != null) {
return setDecision(MVMap.Decision.ABORT);
}
logIt(null);
......@@ -228,8 +229,8 @@ abstract class TxDecisionMaker extends MVMap.DecisionMaker<VersionedValue> {
@SuppressWarnings("unchecked")
@Override
public VersionedValue selectValue(VersionedValue existingValue, VersionedValue providedValue) {
return VersionedValue.getInstance(undoKey,
existingValue == null ? null : existingValue.value,
return VersionedValueUncommitted.getInstance(undoKey,
existingValue == null ? null : existingValue.getCurrentValue(),
existingValue == null ? null : existingValue.getCommittedValue());
}
}
......
/*
* 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.tx;
import org.h2.engine.Constants;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.WriteBuffer;
import org.h2.mvstore.type.DataType;
import java.nio.ByteBuffer;
/**
* A versioned value (possibly null).
* It contains current value and latest committed value if current one is uncommitted.
* Also for uncommitted values it contains operationId - a combination of
* transactionId and logId.
*/
public class VersionedValue {
public static final VersionedValue DUMMY = new VersionedValue(new Object());
/**
* The current value.
*/
public final Object value;
static VersionedValue getInstance(Object value) {
assert value != null;
return new VersionedValue(value);
}
public static VersionedValue getInstance(long operationId, Object value, Object committedValue) {
return new Uncommitted(operationId, value, committedValue);
}
VersionedValue(Object value) {
this.value = value;
}
public boolean isCommitted() {
return true;
}
public long getOperationId() {
return 0L;
}
public Object getCommittedValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
private static class Uncommitted extends VersionedValue
{
private final long operationId;
private final Object committedValue;
Uncommitted(long operationId, Object value, Object committedValue) {
super(value);
assert operationId != 0;
this.operationId = operationId;
this.committedValue = committedValue;
}
@Override
public boolean isCommitted() {
return false;
}
@Override
public long getOperationId() {
return operationId;
}
@Override
public Object getCommittedValue() {
return committedValue;
}
@Override
public String toString() {
return super.toString() +
" " + TransactionStore.getTransactionId(operationId) + "/" +
TransactionStore.getLogId(operationId) + " " + committedValue;
}
}
/**
* The value type for a versioned value.
*/
public static class Type implements DataType {
private final DataType valueType;
public Type(DataType valueType) {
this.valueType = valueType;
}
@Override
public int getMemory(Object obj) {
if(obj == null) return 0;
VersionedValue v = (VersionedValue) obj;
int res = Constants.MEMORY_OBJECT + 8 + 2 * Constants.MEMORY_POINTER +
getValMemory(v.value);
if (v.getOperationId() != 0) {
res += getValMemory(v.getCommittedValue());
}
return res;
}
private int getValMemory(Object obj) {
return obj == null ? 0 : valueType.getMemory(obj);
}
@Override
public int compare(Object aObj, Object bObj) {
if (aObj == bObj) {
return 0;
} else if (aObj == null) {
return -1;
} else if (bObj == null) {
return 1;
}
VersionedValue a = (VersionedValue) aObj;
VersionedValue b = (VersionedValue) bObj;
long comp = a.getOperationId() - b.getOperationId();
if (comp == 0) {
return valueType.compare(a.value, b.value);
}
return Long.signum(comp);
}
@Override
public void read(ByteBuffer buff, Object[] obj, int len, boolean key) {
if (buff.get() == 0) {
// fast path (no op ids or null entries)
for (int i = 0; i < len; i++) {
obj[i] = new VersionedValue(valueType.read(buff));
}
} else {
// slow path (some entries may be null)
for (int i = 0; i < len; i++) {
obj[i] = read(buff);
}
}
}
@Override
public Object read(ByteBuffer buff) {
long operationId = DataUtils.readVarLong(buff);
if (operationId == 0) {
return new VersionedValue(valueType.read(buff));
} else {
byte flags = buff.get();
Object value = (flags & 1) != 0 ? valueType.read(buff) : null;
Object committedValue = (flags & 2) != 0 ? valueType.read(buff) : null;
return new Uncommitted(operationId, value, committedValue);
}
}
@Override
public void write(WriteBuffer buff, Object[] obj, int len, boolean key) {
boolean fastPath = true;
for (int i = 0; i < len; i++) {
VersionedValue v = (VersionedValue) obj[i];
if (v.getOperationId() != 0 || v.value == null) {
fastPath = false;
}
}
if (fastPath) {
buff.put((byte) 0);
for (int i = 0; i < len; i++) {
VersionedValue v = (VersionedValue) obj[i];
valueType.write(buff, v.value);
}
} else {
// slow path:
// store op ids, and some entries may be null
buff.put((byte) 1);
for (int i = 0; i < len; i++) {
write(buff, obj[i]);
}
}
}
@Override
public void write(WriteBuffer buff, Object obj) {
VersionedValue v = (VersionedValue) obj;
long operationId = v.getOperationId();
buff.putVarLong(operationId);
if (operationId == 0) {
valueType.write(buff, v.value);
} else {
Object committedValue = v.getCommittedValue();
int flags = (v.value == null ? 0 : 1) | (committedValue == null ? 0 : 2);
buff.put((byte) flags);
if (v.value != null) {
valueType.write(buff, v.value);
}
if (committedValue != null) {
valueType.write(buff, committedValue);
}
}
}
}
}
/*
* 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.tx;
import org.h2.value.VersionedValue;
/**
* Class CommittedVersionedValue.
*
* @author <a href='mailto:andrei.tokar@gmail.com'>Andrei Tokar</a>
*/
class VersionedValueCommitted extends VersionedValue {
/**
* The current value.
*/
public final Object value;
VersionedValueCommitted(Object value) {
this.value = value;
}
static VersionedValue getInstance(Object value) {
assert value != null;
return value instanceof VersionedValue ? (VersionedValue) value : new VersionedValueCommitted(value);
}
public Object getCurrentValue() {
return value;
}
public Object getCommittedValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
/*
* 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.tx;
import org.h2.engine.Constants;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.WriteBuffer;
import org.h2.mvstore.type.DataType;
import org.h2.value.VersionedValue;
import java.nio.ByteBuffer;
/**
* The value type for a versioned value.
*/
public class VersionedValueType implements DataType {
private final DataType valueType;
public VersionedValueType(DataType valueType) {
this.valueType = valueType;
}
@Override
public int getMemory(Object obj) {
if(obj == null) return 0;
VersionedValue v = (VersionedValue) obj;
int res = Constants.MEMORY_OBJECT + 8 + 2 * Constants.MEMORY_POINTER +
getValMemory(v.getCurrentValue());
if (v.getOperationId() != 0) {
res += getValMemory(v.getCommittedValue());
}
return res;
}
private int getValMemory(Object obj) {
return obj == null ? 0 : valueType.getMemory(obj);
}
@Override
public int compare(Object aObj, Object bObj) {
if (aObj == bObj) {
return 0;
} else if (aObj == null) {
return -1;
} else if (bObj == null) {
return 1;
}
VersionedValue a = (VersionedValue) aObj;
VersionedValue b = (VersionedValue) bObj;
long comp = a.getOperationId() - b.getOperationId();
if (comp == 0) {
return valueType.compare(a.getCurrentValue(), b.getCurrentValue());
}
return Long.signum(comp);
}
@Override
public void read(ByteBuffer buff, Object[] obj, int len, boolean key) {
if (buff.get() == 0) {
// fast path (no op ids or null entries)
for (int i = 0; i < len; i++) {
obj[i] = VersionedValueCommitted.getInstance(valueType.read(buff));
}
} else {
// slow path (some entries may be null)
for (int i = 0; i < len; i++) {
obj[i] = read(buff);
}
}
}
@Override
public Object read(ByteBuffer buff) {
long operationId = DataUtils.readVarLong(buff);
if (operationId == 0) {
return VersionedValueCommitted.getInstance(valueType.read(buff));
} else {
byte flags = buff.get();
Object value = (flags & 1) != 0 ? valueType.read(buff) : null;
Object committedValue = (flags & 2) != 0 ? valueType.read(buff) : null;
return VersionedValueUncommitted.getInstance(operationId, value, committedValue);
}
}
@Override
public void write(WriteBuffer buff, Object[] obj, int len, boolean key) {
boolean fastPath = true;
for (int i = 0; i < len; i++) {
VersionedValue v = (VersionedValue) obj[i];
if (v.getOperationId() != 0 || v.getCurrentValue() == null) {
fastPath = false;
}
}
if (fastPath) {
buff.put((byte) 0);
for (int i = 0; i < len; i++) {
VersionedValue v = (VersionedValue) obj[i];
valueType.write(buff, v.getCurrentValue());
}
} else {
// slow path:
// store op ids, and some entries may be null
buff.put((byte) 1);
for (int i = 0; i < len; i++) {
write(buff, obj[i]);
}
}
}
@Override
public void write(WriteBuffer buff, Object obj) {
VersionedValue v = (VersionedValue) obj;
long operationId = v.getOperationId();
buff.putVarLong(operationId);
if (operationId == 0) {
valueType.write(buff, v.getCurrentValue());
} else {
Object committedValue = v.getCommittedValue();
int flags = (v.getCurrentValue() == null ? 0 : 1) | (committedValue == null ? 0 : 2);
buff.put((byte) flags);
if (v.getCurrentValue() != null) {
valueType.write(buff, v.getCurrentValue());
}
if (committedValue != null) {
valueType.write(buff, committedValue);
}
}
}
}
/*
* 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.tx;
import org.h2.value.VersionedValue;
/**
* Class VersionedValueUncommitted.
*
* @author <a href='mailto:andrei.tokar@gmail.com'>Andrei Tokar</a>
*/
class VersionedValueUncommitted extends VersionedValueCommitted {
private final long operationId;
private final Object committedValue;
private VersionedValueUncommitted(long operationId, Object value, Object committedValue) {
super(value);
assert operationId != 0;
this.operationId = operationId;
this.committedValue = committedValue;
}
static VersionedValue getInstance(long operationId, Object value, Object committedValue) {
return new VersionedValueUncommitted(operationId, value, committedValue);
}
@Override
public boolean isCommitted() {
return false;
}
@Override
public long getOperationId() {
return operationId;
}
@Override
public Object getCommittedValue() {
return committedValue;
}
@Override
public String toString() {
return super.toString() +
" " + TransactionStore.getTransactionId(operationId) + "/" +
TransactionStore.getLogId(operationId) + " " + committedValue;
}
}
......@@ -40,7 +40,7 @@ import org.h2.util.StringUtils;
* @author Noel Grandin
* @author Nicolas Fortin, Atelier SIG, IRSTV FR CNRS 24888
*/
public abstract class Value {
public abstract class Value extends VersionedValue {
/**
* The data type is unknown at this time.
......
/*
* 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.value;
/**
* A versioned value (possibly null).
* It contains current value and latest committed value if current one is uncommitted.
* Also for uncommitted values it contains operationId - a combination of
* transactionId and logId.
*/
public class VersionedValue {
public static final VersionedValue DUMMY = new VersionedValue();
protected VersionedValue() {}
public boolean isCommitted() {
return true;
}
public long getOperationId() {
return 0L;
}
public Object getCurrentValue() {
return this;
}
public Object getCommittedValue() {
return this;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论