This is an automated email from the ASF dual-hosted git repository.
brfrn169 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/master by this push:
new ecbed33 HBASE-23146 Support CheckAndMutate with multiple conditions
(#1114)
ecbed33 is described below
commit ecbed33092ace031ad260026c7676e6c5886c267
Author: Toshihiro Suzuki <[email protected]>
AuthorDate: Wed Feb 26 08:09:04 2020 +0900
HBASE-23146 Support CheckAndMutate with multiple conditions (#1114)
Signed-off-by: Duo Zhang <[email protected]>
---
.../org/apache/hadoop/hbase/client/AsyncTable.java | 55 ++++++
.../apache/hadoop/hbase/client/AsyncTableImpl.java | 31 ++++
.../hadoop/hbase/client/RawAsyncTableImpl.java | 87 +++++++--
.../java/org/apache/hadoop/hbase/client/Table.java | 48 +++++
.../hadoop/hbase/client/TableOverAsyncTable.java | 105 ++++++-----
.../hbase/shaded/protobuf/RequestConverter.java | 89 ++++-----
.../src/main/protobuf/Client.proto | 9 +-
hbase-protocol/src/main/protobuf/Client.proto | 9 +-
.../hadoop/hbase/rest/client/RemoteHTable.java | 7 +
.../hadoop/hbase/coprocessor/RegionObserver.java | 137 +++++++++++++-
.../apache/hadoop/hbase/regionserver/HRegion.java | 81 +++++---
.../hadoop/hbase/regionserver/RSRpcServices.java | 111 +++++++----
.../apache/hadoop/hbase/regionserver/Region.java | 53 ++++++
.../hbase/regionserver/RegionCoprocessorHost.java | 151 ++++++++++++++-
.../hadoop/hbase/client/DummyAsyncTable.java | 6 +
.../apache/hadoop/hbase/client/TestAsyncTable.java | 203 +++++++++++++++++++++
.../hadoop/hbase/client/TestCheckAndMutate.java | 190 +++++++++++++++++++
.../hbase/client/TestMalformedCellFromClient.java | 5 +-
.../hbase/coprocessor/SimpleRegionObserver.java | 97 ++++++++--
.../coprocessor/TestRegionObserverInterface.java | 51 +++++-
.../hadoop/hbase/regionserver/RegionAsTable.java | 6 +
.../hadoop/hbase/regionserver/TestHRegion.java | 123 +++++++++++++
.../hadoop/hbase/thrift2/client/ThriftTable.java | 6 +
23 files changed, 1454 insertions(+), 206 deletions(-)
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTable.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTable.java
index bfcc187..e10f1f82 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTable.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTable.java
@@ -29,6 +29,7 @@ import java.util.function.Function;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.yetus.audience.InterfaceAudience;
@@ -290,6 +291,60 @@ public interface AsyncTable<C extends
ScanResultConsumerBase> {
}
/**
+ * Atomically checks if a row matches the specified filter. If it does, it
adds the
+ * Put/Delete/RowMutations.
+ * <p>
+ * Use the returned {@link CheckAndMutateWithFilterBuilder} to construct
your request and then
+ * execute it. This is a fluent style API, the code is like:
+ *
+ * <pre>
+ * <code>
+ * table.checkAndMutate(row, filter).thenPut(put)
+ * .thenAccept(succ -> {
+ * if (succ) {
+ * System.out.println("Check and put succeeded");
+ * } else {
+ * System.out.println("Check and put failed");
+ * }
+ * });
+ * </code>
+ * </pre>
+ */
+ CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter filter);
+
+ /**
+ * A helper class for sending checkAndMutate request with a filter.
+ */
+ interface CheckAndMutateWithFilterBuilder {
+
+ /**
+ * @param timeRange time range to check.
+ */
+ CheckAndMutateWithFilterBuilder timeRange(TimeRange timeRange);
+
+ /**
+ * @param put data to put if check succeeds
+ * @return {@code true} if the new put was executed, {@code false}
otherwise. The return value
+ * will be wrapped by a {@link CompletableFuture}.
+ */
+ CompletableFuture<Boolean> thenPut(Put put);
+
+ /**
+ * @param delete data to delete if check succeeds
+ * @return {@code true} if the new delete was executed, {@code false}
otherwise. The return
+ * value will be wrapped by a {@link CompletableFuture}.
+ */
+ CompletableFuture<Boolean> thenDelete(Delete delete);
+
+ /**
+ * @param mutation mutations to perform if check succeeds
+ * @return true if the new mutation was executed, false otherwise. The
return value will be
+ * wrapped by a {@link CompletableFuture}.
+ */
+ CompletableFuture<Boolean> thenMutate(RowMutations mutation);
+ }
+
+ /**
* Performs multiple mutations atomically on a single row. Currently {@link
Put} and
* {@link Delete} are supported.
* @param mutation object that specifies the set of mutations to perform
atomically
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTableImpl.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTableImpl.java
index 2256a4c..d6406b6 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTableImpl.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTableImpl.java
@@ -29,6 +29,7 @@ import java.util.function.Function;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.util.FutureUtils;
import org.apache.yetus.audience.InterfaceAudience;
@@ -174,6 +175,36 @@ class AsyncTableImpl implements
AsyncTable<ScanResultConsumer> {
}
@Override
+ public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ return new CheckAndMutateWithFilterBuilder() {
+
+ private final CheckAndMutateWithFilterBuilder builder =
+ rawTable.checkAndMutate(row, filter);
+
+ @Override
+ public CheckAndMutateWithFilterBuilder timeRange(TimeRange timeRange) {
+ builder.timeRange(timeRange);
+ return this;
+ }
+
+ @Override
+ public CompletableFuture<Boolean> thenPut(Put put) {
+ return wrap(builder.thenPut(put));
+ }
+
+ @Override
+ public CompletableFuture<Boolean> thenDelete(Delete delete) {
+ return wrap(builder.thenDelete(delete));
+ }
+
+ @Override
+ public CompletableFuture<Boolean> thenMutate(RowMutations mutation) {
+ return wrap(builder.thenMutate(mutation));
+ }
+ };
+ }
+
+ @Override
public CompletableFuture<Void> mutateRow(RowMutations mutation) {
return wrap(rawTable.mutateRow(mutation));
}
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncTableImpl.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncTableImpl.java
index c357b1f..ebdde40 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncTableImpl.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncTableImpl.java
@@ -41,7 +41,7 @@ import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.TableName;
import
org.apache.hadoop.hbase.client.AsyncRpcRetryingCallerFactory.SingleRequestCallerBuilder;
import org.apache.hadoop.hbase.client.ConnectionUtils.Converter;
-import org.apache.hadoop.hbase.filter.BinaryComparator;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
import org.apache.hadoop.hbase.util.Bytes;
@@ -65,7 +65,6 @@ import
org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MultiRespo
import
org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MutateRequest;
import
org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.MutateResponse;
import
org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionAction;
-import
org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.CompareType;
/**
* The implementation of RawAsyncTable.
@@ -320,10 +319,10 @@ class RawAsyncTableImpl implements
AsyncTable<AdvancedScanResultConsumer> {
validatePut(put, conn.connConf.getMaxKeyValueSize());
preCheck();
return RawAsyncTableImpl.this.<Boolean> newCaller(row,
put.getPriority(), rpcTimeoutNs)
- .action((controller, loc, stub) -> RawAsyncTableImpl.<Put, Boolean>
mutate(controller, loc,
+ .action((controller, loc, stub) ->
RawAsyncTableImpl.mutate(controller, loc,
stub, put,
- (rn, p) -> RequestConverter.buildMutateRequest(rn, row, family,
qualifier,
- new BinaryComparator(value), CompareType.valueOf(op.name()),
timeRange, p),
+ (rn, p) -> RequestConverter.buildMutateRequest(rn, row, family,
qualifier, op, value,
+ null, timeRange, p),
(c, r) -> r.getProcessed()))
.call();
}
@@ -332,10 +331,10 @@ class RawAsyncTableImpl implements
AsyncTable<AdvancedScanResultConsumer> {
public CompletableFuture<Boolean> thenDelete(Delete delete) {
preCheck();
return RawAsyncTableImpl.this.<Boolean> newCaller(row,
delete.getPriority(), rpcTimeoutNs)
- .action((controller, loc, stub) -> RawAsyncTableImpl.<Delete, Boolean>
mutate(controller,
+ .action((controller, loc, stub) -> RawAsyncTableImpl.mutate(controller,
loc, stub, delete,
- (rn, d) -> RequestConverter.buildMutateRequest(rn, row, family,
qualifier,
- new BinaryComparator(value), CompareType.valueOf(op.name()),
timeRange, d),
+ (rn, d) -> RequestConverter.buildMutateRequest(rn, row, family,
qualifier, op, value,
+ null, timeRange, d),
(c, r) -> r.getProcessed()))
.call();
}
@@ -343,12 +342,12 @@ class RawAsyncTableImpl implements
AsyncTable<AdvancedScanResultConsumer> {
@Override
public CompletableFuture<Boolean> thenMutate(RowMutations mutation) {
preCheck();
- return RawAsyncTableImpl.this
- .<Boolean> newCaller(row, mutation.getMaxPriority(), rpcTimeoutNs)
- .action((controller, loc, stub) -> RawAsyncTableImpl.this.<Boolean>
mutateRow(controller,
+ return RawAsyncTableImpl.this.<Boolean> newCaller(row,
mutation.getMaxPriority(),
+ rpcTimeoutNs)
+ .action((controller, loc, stub) ->
RawAsyncTableImpl.this.mutateRow(controller,
loc, stub, mutation,
- (rn, rm) -> RequestConverter.buildMutateRequest(rn, row, family,
qualifier,
- new BinaryComparator(value), CompareType.valueOf(op.name()),
timeRange, rm),
+ (rn, rm) -> RequestConverter.buildMutateRequest(rn, row, family,
qualifier, op, value,
+ null, timeRange, rm),
resp -> resp.getExists()))
.call();
}
@@ -359,6 +358,68 @@ class RawAsyncTableImpl implements
AsyncTable<AdvancedScanResultConsumer> {
return new CheckAndMutateBuilderImpl(row, family);
}
+
+ private final class CheckAndMutateWithFilterBuilderImpl
+ implements CheckAndMutateWithFilterBuilder {
+
+ private final byte[] row;
+
+ private final Filter filter;
+
+ private TimeRange timeRange;
+
+ public CheckAndMutateWithFilterBuilderImpl(byte[] row, Filter filter) {
+ this.row = Preconditions.checkNotNull(row, "row is null");
+ this.filter = Preconditions.checkNotNull(filter, "filter is null");
+ }
+
+ @Override
+ public CheckAndMutateWithFilterBuilder timeRange(TimeRange timeRange) {
+ this.timeRange = timeRange;
+ return this;
+ }
+
+ @Override
+ public CompletableFuture<Boolean> thenPut(Put put) {
+ validatePut(put, conn.connConf.getMaxKeyValueSize());
+ return RawAsyncTableImpl.this.<Boolean> newCaller(row,
put.getPriority(), rpcTimeoutNs)
+ .action((controller, loc, stub) ->
RawAsyncTableImpl.mutate(controller, loc,
+ stub, put,
+ (rn, p) -> RequestConverter.buildMutateRequest(rn, row, null, null,
null, null,
+ filter, timeRange, p),
+ (c, r) -> r.getProcessed()))
+ .call();
+ }
+
+ @Override
+ public CompletableFuture<Boolean> thenDelete(Delete delete) {
+ return RawAsyncTableImpl.this.<Boolean> newCaller(row,
delete.getPriority(), rpcTimeoutNs)
+ .action((controller, loc, stub) -> RawAsyncTableImpl.mutate(controller,
+ loc, stub, delete,
+ (rn, d) -> RequestConverter.buildMutateRequest(rn, row, null, null,
null, null,
+ filter, timeRange, d),
+ (c, r) -> r.getProcessed()))
+ .call();
+ }
+
+ @Override
+ public CompletableFuture<Boolean> thenMutate(RowMutations mutation) {
+ return RawAsyncTableImpl.this.<Boolean> newCaller(row,
mutation.getMaxPriority(),
+ rpcTimeoutNs)
+ .action((controller, loc, stub) ->
RawAsyncTableImpl.this.mutateRow(controller,
+ loc, stub, mutation,
+ (rn, rm) -> RequestConverter.buildMutateRequest(rn, row, null, null,
null, null,
+ filter, timeRange, rm),
+ resp -> resp.getExists()))
+ .call();
+ }
+ }
+
+ @Override
+ public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ return new CheckAndMutateWithFilterBuilderImpl(row, filter);
+ }
+
// We need the MultiRequest when constructing the
org.apache.hadoop.hbase.client.MultiResponse,
// so here I write a new method as I do not want to change the abstraction
of call method.
private <RESP> CompletableFuture<RESP> mutateRow(HBaseRpcController
controller,
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Table.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Table.java
index 41b0e47..e600e77 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Table.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Table.java
@@ -36,6 +36,7 @@ import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.coprocessor.Batch;
import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.util.Bytes;
@@ -355,6 +356,53 @@ public interface Table extends Closeable {
* @return {@code true} if the new delete was executed, {@code false}
otherwise.
*/
boolean thenDelete(Delete delete) throws IOException;
+
+ /**
+ * @param mutation mutations to perform if check succeeds
+ * @return true if the new mutation was executed, false otherwise.
+ */
+ boolean thenMutate(RowMutations mutation) throws IOException;
+ }
+
+ /**
+ * Atomically checks if a row matches the specified filter. If it does, it
adds the
+ * Put/Delete/RowMutations.
+ * <p>
+ * Use the returned {@link CheckAndMutateWithFilterBuilder} to construct
your request and then
+ * execute it. This is a fluent style API, the code is like:
+ *
+ * <pre>
+ * <code>
+ * table.checkAndMutate(row, filter).thenPut(put);
+ * </code>
+ * </pre>
+ */
+ default CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ throw new NotImplementedException("Add an implementation!");
+ }
+
+ /**
+ * A helper class for sending checkAndMutate request with a filter.
+ */
+ interface CheckAndMutateWithFilterBuilder {
+
+ /**
+ * @param timeRange timeRange to check
+ */
+ CheckAndMutateWithFilterBuilder timeRange(TimeRange timeRange);
+
+ /**
+ * @param put data to put if check succeeds
+ * @return {@code true} if the new put was executed, {@code false}
otherwise.
+ */
+ boolean thenPut(Put put) throws IOException;
+
+ /**
+ * @param delete data to delete if check succeeds
+ * @return {@code true} if the new delete was executed, {@code false}
otherwise.
+ */
+ boolean thenDelete(Delete delete) throws IOException;
+
/**
* @param mutation mutations to perform if check succeeds
* @return true if the new mutation was executed, false otherwise.
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/TableOverAsyncTable.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/TableOverAsyncTable.java
index 0a2a66e..283586a 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/TableOverAsyncTable.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/TableOverAsyncTable.java
@@ -52,6 +52,7 @@ import org.apache.hadoop.hbase.TableName;
import
org.apache.hadoop.hbase.client.RetriesExhaustedException.ThrowableWithExtraContext;
import org.apache.hadoop.hbase.client.coprocessor.Batch.Call;
import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.util.Bytes;
@@ -220,58 +221,80 @@ class TableOverAsyncTable implements Table {
FutureUtils.get(table.deleteAll(deletes));
}
- private static final class CheckAndMutateBuilderImpl implements
CheckAndMutateBuilder {
+ @Override
+ public CheckAndMutateBuilder checkAndMutate(byte[] row, byte[] family) {
+ return new CheckAndMutateBuilder() {
- private final AsyncTable.CheckAndMutateBuilder builder;
+ private final AsyncTable.CheckAndMutateBuilder builder =
table.checkAndMutate(row, family);
- public CheckAndMutateBuilderImpl(
- org.apache.hadoop.hbase.client.AsyncTable.CheckAndMutateBuilder
builder) {
- this.builder = builder;
- }
-
- @Override
- public CheckAndMutateBuilder qualifier(byte[] qualifier) {
- builder.qualifier(qualifier);
- return this;
- }
+ @Override
+ public CheckAndMutateBuilder qualifier(byte[] qualifier) {
+ builder.qualifier(qualifier);
+ return this;
+ }
- @Override
- public CheckAndMutateBuilder timeRange(TimeRange timeRange) {
- builder.timeRange(timeRange);
- return this;
- }
+ @Override
+ public CheckAndMutateBuilder timeRange(TimeRange timeRange) {
+ builder.timeRange(timeRange);
+ return this;
+ }
- @Override
- public CheckAndMutateBuilder ifNotExists() {
- builder.ifNotExists();
- return this;
- }
+ @Override
+ public CheckAndMutateBuilder ifNotExists() {
+ builder.ifNotExists();
+ return this;
+ }
- @Override
- public CheckAndMutateBuilder ifMatches(CompareOperator compareOp, byte[]
value) {
- builder.ifMatches(compareOp, value);
- return this;
- }
+ @Override
+ public CheckAndMutateBuilder ifMatches(CompareOperator compareOp, byte[]
value) {
+ builder.ifMatches(compareOp, value);
+ return this;
+ }
- @Override
- public boolean thenPut(Put put) throws IOException {
- return FutureUtils.get(builder.thenPut(put));
- }
+ @Override
+ public boolean thenPut(Put put) throws IOException {
+ return FutureUtils.get(builder.thenPut(put));
+ }
- @Override
- public boolean thenDelete(Delete delete) throws IOException {
- return FutureUtils.get(builder.thenDelete(delete));
- }
+ @Override
+ public boolean thenDelete(Delete delete) throws IOException {
+ return FutureUtils.get(builder.thenDelete(delete));
+ }
- @Override
- public boolean thenMutate(RowMutations mutation) throws IOException {
- return FutureUtils.get(builder.thenMutate(mutation));
- }
+ @Override
+ public boolean thenMutate(RowMutations mutation) throws IOException {
+ return FutureUtils.get(builder.thenMutate(mutation));
+ }
+ };
}
@Override
- public CheckAndMutateBuilder checkAndMutate(byte[] row, byte[] family) {
- return new CheckAndMutateBuilderImpl(table.checkAndMutate(row, family));
+ public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ return new CheckAndMutateWithFilterBuilder() {
+ private final AsyncTable.CheckAndMutateWithFilterBuilder builder =
+ table.checkAndMutate(row, filter);
+
+ @Override
+ public CheckAndMutateWithFilterBuilder timeRange(TimeRange timeRange) {
+ builder.timeRange(timeRange);
+ return this;
+ }
+
+ @Override
+ public boolean thenPut(Put put) throws IOException {
+ return FutureUtils.get(builder.thenPut(put));
+ }
+
+ @Override
+ public boolean thenDelete(Delete delete) throws IOException {
+ return FutureUtils.get(builder.thenDelete(delete));
+ }
+
+ @Override
+ public boolean thenMutate(RowMutations mutation) throws IOException {
+ return FutureUtils.get(builder.thenMutate(mutation));
+ }
+ };
}
@Override
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/RequestConverter.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/RequestConverter.java
index 075829c..4b80f1d 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/RequestConverter.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/RequestConverter.java
@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
import org.apache.hadoop.hbase.CellScannable;
import org.apache.hadoop.hbase.ClusterMetrics.Option;
import org.apache.hadoop.hbase.ClusterMetricsBuilder;
+import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.DoNotRetryIOException;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.NamespaceDescriptor;
@@ -53,7 +54,8 @@ import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableState;
import org.apache.hadoop.hbase.client.replication.ReplicationPeerConfigUtil;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
-import org.apache.hadoop.hbase.filter.ByteArrayComparable;
+import org.apache.hadoop.hbase.filter.BinaryComparator;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.master.RegionState;
import org.apache.hadoop.hbase.replication.ReplicationPeerConfig;
@@ -187,71 +189,51 @@ public final class RequestConverter {
/**
* Create a protocol buffer MutateRequest for a conditioned put
*
- * @param regionName
- * @param row
- * @param family
- * @param qualifier
- * @param comparator
- * @param compareType
- * @param put
* @return a mutate request
* @throws IOException
*/
public static MutateRequest buildMutateRequest(
- final byte[] regionName, final byte[] row, final byte[] family,
- final byte [] qualifier, final ByteArrayComparable comparator,
- final CompareType compareType, TimeRange timeRange, final Put put)
throws IOException {
- return buildMutateRequest(regionName, row, family, qualifier, comparator,
compareType, timeRange
- , put, MutationType.PUT);
+ final byte[] regionName, final byte[] row, final byte[] family,
+ final byte [] qualifier, final CompareOperator op, final byte[] value,
final Filter filter,
+ final TimeRange timeRange, final Put put) throws IOException {
+ return buildMutateRequest(regionName, row, family, qualifier, op, value,
filter, timeRange,
+ put, MutationType.PUT);
}
/**
* Create a protocol buffer MutateRequest for a conditioned delete
*
- * @param regionName
- * @param row
- * @param family
- * @param qualifier
- * @param comparator
- * @param compareType
- * @param delete
* @return a mutate request
* @throws IOException
*/
public static MutateRequest buildMutateRequest(
- final byte[] regionName, final byte[] row, final byte[] family,
- final byte [] qualifier, final ByteArrayComparable comparator,
- final CompareType compareType, TimeRange timeRange, final Delete delete)
throws IOException {
- return buildMutateRequest(regionName, row, family, qualifier, comparator,
compareType, timeRange
- , delete, MutationType.DELETE);
+ final byte[] regionName, final byte[] row, final byte[] family,
+ final byte [] qualifier, final CompareOperator op, final byte[] value,
final Filter filter,
+ final TimeRange timeRange, final Delete delete) throws IOException {
+ return buildMutateRequest(regionName, row, family, qualifier, op, value,
filter, timeRange,
+ delete, MutationType.DELETE);
}
public static MutateRequest buildMutateRequest(final byte[] regionName,
final byte[] row,
- final byte[] family, final byte[] qualifier, final ByteArrayComparable
comparator,
- final CompareType compareType, TimeRange timeRange, final Mutation
mutation,
+ final byte[] family, final byte[] qualifier, final CompareOperator op,
final byte[] value,
+ final Filter filter, final TimeRange timeRange, final Mutation mutation,
final MutationType type) throws IOException {
return MutateRequest.newBuilder()
.setRegion(buildRegionSpecifier(RegionSpecifierType.REGION_NAME,
regionName))
.setMutation(ProtobufUtil.toMutation(type, mutation))
- .setCondition(buildCondition(row, family, qualifier, comparator,
compareType, timeRange))
+ .setCondition(buildCondition(row, family, qualifier, op, value, filter,
timeRange))
.build();
}
+
/**
* Create a protocol buffer MutateRequest for conditioned row mutations
*
- * @param regionName
- * @param row
- * @param family
- * @param qualifier
- * @param comparator
- * @param compareType
- * @param rowMutations
* @return a mutate request
* @throws IOException
*/
public static ClientProtos.MultiRequest buildMutateRequest(final byte[]
regionName,
final byte[] row, final byte[] family, final byte[] qualifier,
- final ByteArrayComparable comparator, final CompareType compareType, final
TimeRange timeRange,
+ final CompareOperator op, final byte[] value, final Filter filter, final
TimeRange timeRange,
final RowMutations rowMutations) throws IOException {
RegionAction.Builder builder =
getRegionActionBuilderWithRegion(RegionAction.newBuilder(),
regionName);
@@ -259,7 +241,7 @@ public final class RequestConverter {
ClientProtos.Action.Builder actionBuilder =
ClientProtos.Action.newBuilder();
MutationProto.Builder mutationBuilder = MutationProto.newBuilder();
for (Mutation mutation: rowMutations.getMutations()) {
- MutationType mutateType = null;
+ MutationType mutateType;
if (mutation instanceof Put) {
mutateType = MutationType.PUT;
} else if (mutation instanceof Delete) {
@@ -275,7 +257,7 @@ public final class RequestConverter {
builder.addAction(actionBuilder.build());
}
return
ClientProtos.MultiRequest.newBuilder().addRegionAction(builder.build())
- .setCondition(buildCondition(row, family, qualifier, comparator,
compareType, timeRange))
+ .setCondition(buildCondition(row, family, qualifier, op, value,
filter, timeRange))
.build();
}
@@ -918,25 +900,26 @@ public final class RequestConverter {
/**
* Create a protocol buffer Condition
*
- * @param row
- * @param family
- * @param qualifier
- * @param comparator
- * @param compareType
* @return a Condition
* @throws IOException
*/
public static Condition buildCondition(final byte[] row, final byte[] family,
- final byte[] qualifier, final ByteArrayComparable comparator, final
CompareType compareType,
- final TimeRange timeRange) {
- return Condition.newBuilder().setRow(UnsafeByteOperations.unsafeWrap(row))
- .setFamily(UnsafeByteOperations.unsafeWrap(family))
- .setQualifier(UnsafeByteOperations.unsafeWrap(qualifier == null ?
- HConstants.EMPTY_BYTE_ARRAY : qualifier))
- .setComparator(ProtobufUtil.toComparator(comparator))
- .setCompareType(compareType)
- .setTimeRange(ProtobufUtil.toTimeRange(timeRange))
- .build();
+ final byte[] qualifier, final CompareOperator op, final byte[] value,
final Filter filter,
+ final TimeRange timeRange) throws IOException {
+
+ Condition.Builder builder =
Condition.newBuilder().setRow(UnsafeByteOperations.unsafeWrap(row));
+
+ if (filter != null) {
+ builder.setFilter(ProtobufUtil.toFilter(filter));
+ } else {
+ builder.setFamily(UnsafeByteOperations.unsafeWrap(family))
+ .setQualifier(UnsafeByteOperations.unsafeWrap(
+ qualifier == null ? HConstants.EMPTY_BYTE_ARRAY : qualifier))
+ .setComparator(ProtobufUtil.toComparator(new BinaryComparator(value)))
+ .setCompareType(CompareType.valueOf(op.name()));
+ }
+
+ return builder.setTimeRange(ProtobufUtil.toTimeRange(timeRange)).build();
}
/**
diff --git a/hbase-protocol-shaded/src/main/protobuf/Client.proto
b/hbase-protocol-shaded/src/main/protobuf/Client.proto
index a22c623..810aaaa 100644
--- a/hbase-protocol-shaded/src/main/protobuf/Client.proto
+++ b/hbase-protocol-shaded/src/main/protobuf/Client.proto
@@ -139,11 +139,12 @@ message GetResponse {
*/
message Condition {
required bytes row = 1;
- required bytes family = 2;
- required bytes qualifier = 3;
- required CompareType compare_type = 4;
- required Comparator comparator = 5;
+ optional bytes family = 2;
+ optional bytes qualifier = 3;
+ optional CompareType compare_type = 4;
+ optional Comparator comparator = 5;
optional TimeRange time_range = 6;
+ optional Filter filter = 7;
}
diff --git a/hbase-protocol/src/main/protobuf/Client.proto
b/hbase-protocol/src/main/protobuf/Client.proto
index 5fd20c8..59cb0e2 100644
--- a/hbase-protocol/src/main/protobuf/Client.proto
+++ b/hbase-protocol/src/main/protobuf/Client.proto
@@ -138,11 +138,12 @@ message GetResponse {
*/
message Condition {
required bytes row = 1;
- required bytes family = 2;
- required bytes qualifier = 3;
- required CompareType compare_type = 4;
- required Comparator comparator = 5;
+ optional bytes family = 2;
+ optional bytes qualifier = 3;
+ optional CompareType compare_type = 4;
+ optional Comparator comparator = 5;
optional TimeRange time_range = 6;
+ optional Filter filter = 7;
}
diff --git
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
index 9e79c06..c3fc819 100644
---
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
+++
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
@@ -35,6 +35,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.NotImplementedException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
@@ -60,6 +61,7 @@ import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.coprocessor.Batch;
import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.rest.Constants;
@@ -738,6 +740,11 @@ public class RemoteHTable implements Table {
}
@Override
+ public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ throw new NotImplementedException("Implement later");
+ }
+
+ @Override
public Result increment(Increment increment) throws IOException {
throw new IOException("Increment not supported");
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
index 8761d6b..05071fc 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
@@ -40,6 +40,7 @@ import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
import org.apache.hadoop.hbase.io.Reference;
import org.apache.hadoop.hbase.io.hfile.CacheConfig;
@@ -511,9 +512,8 @@ public interface RegionObserver {
* @param op the comparison operation
* @param comparator the comparator
* @param put data to put if check succeeds
- * @param result
- * @return the return value to return to client if bypassing default
- * processing
+ * @param result the default value of the result
+ * @return the return value to return to client if bypassing default
processing
*/
default boolean preCheckAndPut(ObserverContext<RegionCoprocessorEnvironment>
c, byte[] row,
byte[] family, byte[] qualifier, CompareOperator op, ByteArrayComparable
comparator, Put put,
@@ -522,6 +522,26 @@ public interface RegionObserver {
}
/**
+ * Called before checkAndPut.
+ * <p>
+ * Call CoprocessorEnvironment#bypass to skip default actions.
+ * If 'bypass' is set, we skip out on calling any subsequent chained
coprocessors.
+ * <p>
+ * Note: Do not retain references to any Cells in 'put' beyond the life of
this invocation.
+ * If need a Cell reference for later use, copy the cell and use that.
+ * @param c the environment provided by the region server
+ * @param row row to check
+ * @param filter filter
+ * @param put data to put if check succeeds
+ * @param result the default value of the result
+ * @return the return value to return to client if bypassing default
processing
+ */
+ default boolean preCheckAndPut(ObserverContext<RegionCoprocessorEnvironment>
c, byte[] row,
+ Filter filter, Put put, boolean result) throws IOException {
+ return result;
+ }
+
+ /**
* Called before checkAndPut but after acquiring rowlock.
* <p>
* <b>Note:</b> Caution to be taken for not doing any long time operation in
this hook.
@@ -540,9 +560,8 @@ public interface RegionObserver {
* @param op the comparison operation
* @param comparator the comparator
* @param put data to put if check succeeds
- * @param result
- * @return the return value to return to client if bypassing default
- * processing
+ * @param result the default value of the result
+ * @return the return value to return to client if bypassing default
processing
*/
default boolean
preCheckAndPutAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> c,
byte[] row, byte[] family, byte[] qualifier, CompareOperator op,
@@ -551,6 +570,30 @@ public interface RegionObserver {
}
/**
+ * Called before checkAndPut but after acquiring rowlock.
+ * <p>
+ * <b>Note:</b> Caution to be taken for not doing any long time operation in
this hook.
+ * Row will be locked for longer time. Trying to acquire lock on another
row, within this,
+ * can lead to potential deadlock.
+ * <p>
+ * Call CoprocessorEnvironment#bypass to skip default actions.
+ * If 'bypass' is set, we skip out on calling any subsequent chained
coprocessors.
+ * <p>
+ * Note: Do not retain references to any Cells in 'put' beyond the life of
this invocation.
+ * If need a Cell reference for later use, copy the cell and use that.
+ * @param c the environment provided by the region server
+ * @param row row to check
+ * @param filter filter
+ * @param put data to put if check succeeds
+ * @param result the default value of the result
+ * @return the return value to return to client if bypassing default
processing
+ */
+ default boolean
preCheckAndPutAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> c,
+ byte[] row, Filter filter, Put put, boolean result) throws IOException {
+ return result;
+ }
+
+ /**
* Called after checkAndPut
* <p>
* Note: Do not retain references to any Cells in 'put' beyond the life of
this invocation.
@@ -572,6 +615,23 @@ public interface RegionObserver {
}
/**
+ * Called after checkAndPut
+ * <p>
+ * Note: Do not retain references to any Cells in 'put' beyond the life of
this invocation.
+ * If need a Cell reference for later use, copy the cell and use that.
+ * @param c the environment provided by the region server
+ * @param row row to check
+ * @param filter filter
+ * @param put data to put if check succeeds
+ * @param result from the checkAndPut
+ * @return the possibly transformed return value to return to client
+ */
+ default boolean
postCheckAndPut(ObserverContext<RegionCoprocessorEnvironment> c, byte[] row,
+ Filter filter, Put put, boolean result) throws IOException {
+ return result;
+ }
+
+ /**
* Called before checkAndDelete.
* <p>
* Call CoprocessorEnvironment#bypass to skip default actions.
@@ -586,7 +646,7 @@ public interface RegionObserver {
* @param op the comparison operation
* @param comparator the comparator
* @param delete delete to commit if check succeeds
- * @param result
+ * @param result the default value of the result
* @return the value to return to client if bypassing default processing
*/
default boolean
preCheckAndDelete(ObserverContext<RegionCoprocessorEnvironment> c, byte[] row,
@@ -596,6 +656,26 @@ public interface RegionObserver {
}
/**
+ * Called before checkAndDelete.
+ * <p>
+ * Call CoprocessorEnvironment#bypass to skip default actions.
+ * If 'bypass' is set, we skip out on calling any subsequent chained
coprocessors.
+ * <p>
+ * Note: Do not retain references to any Cells in 'delete' beyond the life
of this invocation.
+ * If need a Cell reference for later use, copy the cell and use that.
+ * @param c the environment provided by the region server
+ * @param row row to check
+ * @param filter column family
+ * @param delete delete to commit if check succeeds
+ * @param result the default value of the result
+ * @return the value to return to client if bypassing default processing
+ */
+ default boolean
preCheckAndDelete(ObserverContext<RegionCoprocessorEnvironment> c, byte[] row,
+ Filter filter, Delete delete, boolean result) throws IOException {
+ return result;
+ }
+
+ /**
* Called before checkAndDelete but after acquiring rowock.
* <p>
* <b>Note:</b> Caution to be taken for not doing any long time operation in
this hook.
@@ -614,7 +694,7 @@ public interface RegionObserver {
* @param op the comparison operation
* @param comparator the comparator
* @param delete delete to commit if check succeeds
- * @param result
+ * @param result the default value of the result
* @return the value to return to client if bypassing default processing
*/
default boolean
preCheckAndDeleteAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> c,
@@ -624,6 +704,30 @@ public interface RegionObserver {
}
/**
+ * Called before checkAndDelete but after acquiring rowock.
+ * <p>
+ * <b>Note:</b> Caution to be taken for not doing any long time operation in
this hook.
+ * Row will be locked for longer time. Trying to acquire lock on another
row, within this,
+ * can lead to potential deadlock.
+ * <p>
+ * Call CoprocessorEnvironment#bypass to skip default actions.
+ * If 'bypass' is set, we skip out on calling any subsequent chained
coprocessors.
+ * <p>
+ * Note: Do not retain references to any Cells in 'delete' beyond the life
of this invocation.
+ * If need a Cell reference for later use, copy the cell and use that.
+ * @param c the environment provided by the region server
+ * @param row row to check
+ * @param filter filter
+ * @param delete delete to commit if check succeeds
+ * @param result the default value of the result
+ * @return the value to return to client if bypassing default processing
+ */
+ default boolean
preCheckAndDeleteAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> c,
+ byte[] row, Filter filter, Delete delete, boolean result) throws
IOException {
+ return result;
+ }
+
+ /**
* Called after checkAndDelete
* <p>
* Note: Do not retain references to any Cells in 'delete' beyond the life
of this invocation.
@@ -645,6 +749,23 @@ public interface RegionObserver {
}
/**
+ * Called after checkAndDelete
+ * <p>
+ * Note: Do not retain references to any Cells in 'delete' beyond the life
of this invocation.
+ * If need a Cell reference for later use, copy the cell and use that.
+ * @param c the environment provided by the region server
+ * @param row row to check
+ * @param filter filter
+ * @param delete delete to commit if check succeeds
+ * @param result from the CheckAndDelete
+ * @return the possibly transformed returned value to return to client
+ */
+ default boolean
postCheckAndDelete(ObserverContext<RegionCoprocessorEnvironment> c, byte[] row,
+ Filter filter, Delete delete, boolean result) throws IOException {
+ return result;
+ }
+
+ /**
* Called before Append.
* <p>
* Call CoprocessorEnvironment#bypass to skip default actions.
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
index e1c55ec..c0ad87c 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
@@ -127,6 +127,7 @@ import
org.apache.hadoop.hbase.exceptions.FailedSanityCheckException;
import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
import org.apache.hadoop.hbase.exceptions.UnknownProtocolException;
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterWrapper;
import org.apache.hadoop.hbase.filter.IncompatibleFilterException;
import org.apache.hadoop.hbase.io.HFileLink;
@@ -162,8 +163,6 @@ import org.apache.hadoop.hbase.trace.TraceUtil;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.CancelableProgressable;
import org.apache.hadoop.hbase.util.ClassSize;
-import org.apache.hadoop.hbase.util.CompressionTest;
-import org.apache.hadoop.hbase.util.EncryptionTest;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.FSUtils;
import org.apache.hadoop.hbase.util.HashedBytes;
@@ -4205,13 +4204,26 @@ public class HRegion implements HeapSize,
PropagatingConfigurationObserver, Regi
public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier,
CompareOperator op,
ByteArrayComparable comparator, TimeRange timeRange, Mutation mutation)
throws IOException {
checkMutationType(mutation, row);
- return doCheckAndRowMutate(row, family, qualifier, op, comparator,
timeRange, null, mutation);
+ return doCheckAndRowMutate(row, family, qualifier, op, comparator, null,
timeRange, null,
+ mutation);
+ }
+
+ @Override
+ public boolean checkAndMutate(byte[] row, Filter filter, TimeRange
timeRange, Mutation mutation)
+ throws IOException {
+ return doCheckAndRowMutate(row, null, null, null, null, filter, timeRange,
null, mutation);
}
@Override
public boolean checkAndRowMutate(byte[] row, byte[] family, byte[]
qualifier, CompareOperator op,
ByteArrayComparable comparator, TimeRange timeRange, RowMutations rm)
throws IOException {
- return doCheckAndRowMutate(row, family, qualifier, op, comparator,
timeRange, rm, null);
+ return doCheckAndRowMutate(row, family, qualifier, op, comparator, null,
timeRange, rm, null);
+ }
+
+ @Override
+ public boolean checkAndRowMutate(byte[] row, Filter filter, TimeRange
timeRange, RowMutations rm)
+ throws IOException {
+ return doCheckAndRowMutate(row, null, null, null, null, filter, timeRange,
rm, null);
}
/**
@@ -4219,7 +4231,7 @@ public class HRegion implements HeapSize,
PropagatingConfigurationObserver, Regi
* switches in the few places where there is deviation.
*/
private boolean doCheckAndRowMutate(byte[] row, byte[] family, byte[]
qualifier,
- CompareOperator op, ByteArrayComparable comparator, TimeRange timeRange,
+ CompareOperator op, ByteArrayComparable comparator, Filter filter,
TimeRange timeRange,
RowMutations rowMutations, Mutation mutation)
throws IOException {
// Could do the below checks but seems wacky with two callers only. Just
comment out for now.
@@ -4233,8 +4245,13 @@ public class HRegion implements HeapSize,
PropagatingConfigurationObserver, Regi
startRegionOperation();
try {
Get get = new Get(row);
- checkFamily(family);
- get.addColumn(family, qualifier);
+ if (family != null) {
+ checkFamily(family);
+ get.addColumn(family, qualifier);
+ }
+ if (filter != null) {
+ get.setFilter(filter);
+ }
if (timeRange != null) {
get.setTimeRange(timeRange.getMin(), timeRange.getMax());
}
@@ -4246,11 +4263,23 @@ public class HRegion implements HeapSize,
PropagatingConfigurationObserver, Regi
// Call coprocessor.
Boolean processed = null;
if (mutation instanceof Put) {
- processed =
this.getCoprocessorHost().preCheckAndPutAfterRowLock(row, family,
- qualifier, op, comparator, (Put)mutation);
+ if (filter != null) {
+ processed = this.getCoprocessorHost()
+ .preCheckAndPutAfterRowLock(row, filter, (Put) mutation);
+ } else {
+ processed = this.getCoprocessorHost()
+ .preCheckAndPutAfterRowLock(row, family, qualifier, op,
comparator,
+ (Put) mutation);
+ }
} else if (mutation instanceof Delete) {
- processed =
this.getCoprocessorHost().preCheckAndDeleteAfterRowLock(row, family,
- qualifier, op, comparator, (Delete)mutation);
+ if (filter != null) {
+ processed = this.getCoprocessorHost()
+ .preCheckAndDeleteAfterRowLock(row, filter, (Delete) mutation);
+ } else {
+ processed = this.getCoprocessorHost()
+ .preCheckAndDeleteAfterRowLock(row, family, qualifier, op,
comparator,
+ (Delete) mutation);
+ }
}
if (processed != null) {
return processed;
@@ -4260,20 +4289,28 @@ public class HRegion implements HeapSize,
PropagatingConfigurationObserver, Regi
// Supposition is that now all changes are done under row locks, then
when we go to read,
// we'll get the latest on this row.
List<Cell> result = get(get, false);
- boolean valueIsNull = comparator.getValue() == null ||
comparator.getValue().length == 0;
boolean matches = false;
long cellTs = 0;
- if (result.isEmpty() && valueIsNull) {
- matches = true;
- } else if (result.size() > 0 && result.get(0).getValueLength() == 0 &&
valueIsNull) {
- matches = true;
- cellTs = result.get(0).getTimestamp();
- } else if (result.size() == 1 && !valueIsNull) {
- Cell kv = result.get(0);
- cellTs = kv.getTimestamp();
- int compareResult = PrivateCellUtil.compareValue(kv, comparator);
- matches = matches(op, compareResult);
+ if (filter != null) {
+ if (!result.isEmpty()) {
+ matches = true;
+ cellTs = result.get(0).getTimestamp();
+ }
+ } else {
+ boolean valueIsNull = comparator.getValue() == null ||
comparator.getValue().length == 0;
+ if (result.isEmpty() && valueIsNull) {
+ matches = true;
+ } else if (result.size() > 0 && result.get(0).getValueLength() == 0
&& valueIsNull) {
+ matches = true;
+ cellTs = result.get(0).getTimestamp();
+ } else if (result.size() == 1 && !valueIsNull) {
+ Cell kv = result.get(0);
+ cellTs = kv.getTimestamp();
+ int compareResult = PrivateCellUtil.compareValue(kv, comparator);
+ matches = matches(op, compareResult);
+ }
}
+
// If matches put the new put or delete the new delete
if (matches) {
// We have acquired the row lock already. If the system clock is NOT
monotonically
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
index 21e8313..b2404ca 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
@@ -88,6 +88,7 @@ import
org.apache.hadoop.hbase.exceptions.OutOfOrderScannerNextException;
import org.apache.hadoop.hbase.exceptions.ScannerResetException;
import org.apache.hadoop.hbase.exceptions.UnknownProtocolException;
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.ByteBuffAllocator;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.io.hfile.BlockCache;
@@ -609,9 +610,10 @@ public class RSRpcServices implements HBaseRPCErrorHandler,
* @param cellScanner if non-null, the mutation data -- the Cell content.
*/
private boolean checkAndRowMutate(final HRegion region, final
List<ClientProtos.Action> actions,
- final CellScanner cellScanner, byte[] row, byte[] family, byte[]
qualifier, CompareOperator op,
- ByteArrayComparable comparator, TimeRange timeRange,
RegionActionResult.Builder builder,
- ActivePolicyEnforcement spaceQuotaEnforcement) throws IOException {
+ final CellScanner cellScanner, byte[] row, byte[] family, byte[] qualifier,
+ CompareOperator op, ByteArrayComparable comparator, Filter filter,
TimeRange timeRange,
+ RegionActionResult.Builder builder, ActivePolicyEnforcement
spaceQuotaEnforcement)
+ throws IOException {
int countOfCompleteMutation = 0;
try {
if (!region.getRegionInfo().isMetaRegion()) {
@@ -654,7 +656,12 @@ public class RSRpcServices implements HBaseRPCErrorHandler,
builder.addResultOrException(
resultOrExceptionOrBuilder.build());
}
- return region.checkAndRowMutate(row, family, qualifier, op, comparator,
timeRange, rm);
+
+ if (filter != null) {
+ return region.checkAndRowMutate(row, filter, timeRange, rm);
+ } else {
+ return region.checkAndRowMutate(row, family, qualifier, op,
comparator, timeRange, rm);
+ }
} finally {
// Currently, the checkAndMutate isn't supported by batch so it won't
mess up the cell scanner
// even if the malformed cells are not skipped.
@@ -2775,18 +2782,21 @@ public class RSRpcServices implements
HBaseRPCErrorHandler,
if (request.hasCondition()) {
Condition condition = request.getCondition();
byte[] row = condition.getRow().toByteArray();
- byte[] family = condition.getFamily().toByteArray();
- byte[] qualifier = condition.getQualifier().toByteArray();
- CompareOperator op =
- CompareOperator.valueOf(condition.getCompareType().name());
- ByteArrayComparable comparator =
- ProtobufUtil.toComparator(condition.getComparator());
+ byte[] family = condition.hasFamily() ?
condition.getFamily().toByteArray() : null;
+ byte[] qualifier = condition.hasQualifier() ?
+ condition.getQualifier().toByteArray() : null;
+ CompareOperator op = condition.hasCompareType() ?
+ CompareOperator.valueOf(condition.getCompareType().name()) :
null;
+ ByteArrayComparable comparator = condition.hasComparator() ?
+ ProtobufUtil.toComparator(condition.getComparator()) : null;
+ Filter filter = condition.hasFilter() ?
+ ProtobufUtil.toFilter(condition.getFilter()) : null;
TimeRange timeRange = condition.hasTimeRange() ?
ProtobufUtil.toTimeRange(condition.getTimeRange()) :
TimeRange.allTime();
processed =
checkAndRowMutate(region, regionAction.getActionList(),
cellScanner, row, family,
- qualifier, op, comparator, timeRange,
regionActionResultBuilder,
+ qualifier, op, comparator, filter, timeRange,
regionActionResultBuilder,
spaceQuotaEnforcement);
} else {
doAtomicBatchOp(regionActionResultBuilder, region, quota,
regionAction.getActionList(),
@@ -2935,24 +2945,41 @@ public class RSRpcServices implements
HBaseRPCErrorHandler,
if (request.hasCondition()) {
Condition condition = request.getCondition();
byte[] row = condition.getRow().toByteArray();
- byte[] family = condition.getFamily().toByteArray();
- byte[] qualifier = condition.getQualifier().toByteArray();
- CompareOperator compareOp =
- CompareOperator.valueOf(condition.getCompareType().name());
- ByteArrayComparable comparator =
ProtobufUtil.toComparator(condition.getComparator());
+ byte[] family = condition.hasFamily() ?
condition.getFamily().toByteArray() : null;
+ byte[] qualifier = condition.hasQualifier() ?
+ condition.getQualifier().toByteArray() : null;
+ CompareOperator op = condition.hasCompareType() ?
+ CompareOperator.valueOf(condition.getCompareType().name()) :
null;
+ ByteArrayComparable comparator = condition.hasComparator() ?
+ ProtobufUtil.toComparator(condition.getComparator()) : null;
+ Filter filter = condition.hasFilter() ?
+ ProtobufUtil.toFilter(condition.getFilter()) : null;
TimeRange timeRange = condition.hasTimeRange() ?
ProtobufUtil.toTimeRange(condition.getTimeRange()) :
TimeRange.allTime();
if (region.getCoprocessorHost() != null) {
- processed = region.getCoprocessorHost().preCheckAndPut(row,
family, qualifier,
- compareOp, comparator, put);
+ if (filter != null) {
+ processed = region.getCoprocessorHost().preCheckAndPut(row,
filter, put);
+ } else {
+ processed = region.getCoprocessorHost()
+ .preCheckAndPut(row, family, qualifier, op, comparator, put);
+ }
}
if (processed == null) {
- boolean result = region.checkAndMutate(row, family,
- qualifier, compareOp, comparator, timeRange, put);
+ boolean result;
+ if (filter != null) {
+ result = region.checkAndMutate(row, filter, timeRange, put);
+ } else {
+ result = region.checkAndMutate(row, family, qualifier, op,
comparator, timeRange,
+ put);
+ }
if (region.getCoprocessorHost() != null) {
- result = region.getCoprocessorHost().postCheckAndPut(row,
family,
- qualifier, compareOp, comparator, put, result);
+ if (filter != null) {
+ result = region.getCoprocessorHost().postCheckAndPut(row,
filter, put, result);
+ } else {
+ result = region.getCoprocessorHost()
+ .postCheckAndPut(row, family, qualifier, op, comparator,
put, result);
+ }
}
processed = result;
}
@@ -2969,23 +2996,42 @@ public class RSRpcServices implements
HBaseRPCErrorHandler,
if (request.hasCondition()) {
Condition condition = request.getCondition();
byte[] row = condition.getRow().toByteArray();
- byte[] family = condition.getFamily().toByteArray();
- byte[] qualifier = condition.getQualifier().toByteArray();
- CompareOperator op =
CompareOperator.valueOf(condition.getCompareType().name());
- ByteArrayComparable comparator =
ProtobufUtil.toComparator(condition.getComparator());
+ byte[] family = condition.hasFamily() ?
condition.getFamily().toByteArray() : null;
+ byte[] qualifier = condition.hasQualifier() ?
+ condition.getQualifier().toByteArray() : null;
+ CompareOperator op = condition.hasCompareType() ?
+ CompareOperator.valueOf(condition.getCompareType().name()) :
null;
+ ByteArrayComparable comparator = condition.hasComparator() ?
+ ProtobufUtil.toComparator(condition.getComparator()) : null;
+ Filter filter = condition.hasFilter() ?
+ ProtobufUtil.toFilter(condition.getFilter()) : null;
TimeRange timeRange = condition.hasTimeRange() ?
ProtobufUtil.toTimeRange(condition.getTimeRange()) :
TimeRange.allTime();
if (region.getCoprocessorHost() != null) {
- processed = region.getCoprocessorHost().preCheckAndDelete(row,
family, qualifier, op,
- comparator, delete);
+ if (filter != null) {
+ processed = region.getCoprocessorHost().preCheckAndDelete(row,
filter, delete);
+ } else {
+ processed = region.getCoprocessorHost()
+ .preCheckAndDelete(row, family, qualifier, op, comparator,
delete);
+ }
}
if (processed == null) {
- boolean result = region.checkAndMutate(row, family,
- qualifier, op, comparator, timeRange, delete);
+ boolean result;
+ if (filter != null) {
+ result = region.checkAndMutate(row, filter, timeRange, delete);
+ } else {
+ result = region.checkAndMutate(row, family, qualifier, op,
comparator, timeRange,
+ delete);
+ }
if (region.getCoprocessorHost() != null) {
- result = region.getCoprocessorHost().postCheckAndDelete(row,
family,
- qualifier, op, comparator, delete, result);
+ if (filter != null) {
+ result = region.getCoprocessorHost().postCheckAndDelete(row,
filter, delete,
+ result);
+ } else {
+ result = region.getCoprocessorHost()
+ .postCheckAndDelete(row, family, qualifier, op,
comparator, delete, result);
+ }
}
processed = result;
}
@@ -3036,7 +3082,6 @@ public class RSRpcServices implements
HBaseRPCErrorHandler,
break;
default:
break;
-
}
}
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/Region.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/Region.java
index e684c26..8478a73 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/Region.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/Region.java
@@ -41,6 +41,7 @@ import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.conf.ConfigurationObserver;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import
org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker;
import org.apache.yetus.audience.InterfaceAudience;
@@ -334,6 +335,31 @@ public interface Region extends ConfigurationObserver {
ByteArrayComparable comparator, TimeRange timeRange, Mutation mutation)
throws IOException;
/**
+ * Atomically checks if a row matches the filter and if it does, it performs
the mutation. See
+ * checkAndRowMutate to do many checkAndPuts at a time on a single row.
+ * @param row to check
+ * @param filter the filter
+ * @param mutation data to put if check succeeds
+ * @return true if mutation was applied, false otherwise
+ */
+ default boolean checkAndMutate(byte [] row, Filter filter, Mutation mutation)
+ throws IOException {
+ return checkAndMutate(row, filter, TimeRange.allTime(), mutation);
+ }
+
+ /**
+ * Atomically checks if a row value matches the filter and if it does, it
performs the mutation.
+ * See checkAndRowMutate to do many checkAndPuts at a time on a single row.
+ * @param row to check
+ * @param filter the filter
+ * @param mutation data to put if check succeeds
+ * @param timeRange time range to check
+ * @return true if mutation was applied, false otherwise
+ */
+ boolean checkAndMutate(byte [] row, Filter filter, TimeRange timeRange,
Mutation mutation)
+ throws IOException;
+
+ /**
* Atomically checks if a row/family/qualifier value matches the expected
values and if it does,
* it performs the row mutations. If the passed value is null, the lack of
column value
* (ie: non-existence) is used. Use to do many mutations on a single row.
Use checkAndMutate
@@ -371,6 +397,33 @@ public interface Region extends ConfigurationObserver {
throws IOException;
/**
+ * Atomically checks if a row matches the filter and if it does, it performs
the row mutations.
+ * Use to do many mutations on a single row. Use checkAndMutate to do one
checkAndMutate at a
+ * time.
+ * @param row to check
+ * @param filter the filter
+ * @param mutations data to put if check succeeds
+ * @return true if mutations were applied, false otherwise
+ */
+ default boolean checkAndRowMutate(byte[] row, Filter filter, RowMutations
mutations)
+ throws IOException {
+ return checkAndRowMutate(row, filter, TimeRange.allTime(), mutations);
+ }
+
+ /**
+ * Atomically checks if a row matches the filter and if it does, it performs
the row mutations.
+ * Use to do many mutations on a single row. Use checkAndMutate to do one
checkAndMutate at a
+ * time.
+ * @param row to check
+ * @param filter the filter
+ * @param mutations data to put if check succeeds
+ * @param timeRange time range to check
+ * @return true if mutations were applied, false otherwise
+ */
+ boolean checkAndRowMutate(byte [] row, Filter filter, TimeRange timeRange,
+ RowMutations mutations) throws IOException;
+
+ /**
* Deletes the specified cells/row.
* @param delete
* @throws IOException
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
index 4468f57..67f0133 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
@@ -68,6 +68,7 @@ import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.coprocessor.RegionObserver;
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
import org.apache.hadoop.hbase.io.Reference;
import org.apache.hadoop.hbase.io.hfile.CacheConfig;
@@ -1082,13 +1083,38 @@ public class RegionCoprocessorHost
/**
* Supports Coprocessor 'bypass'.
* @param row row to check
+ * @param filter filter
+ * @param put data to put if check succeeds
+ * @return true or false to return to client if default processing should be
bypassed, or null
+ * otherwise
+ */
+ public Boolean preCheckAndPut(final byte [] row, final Filter filter, final
Put put)
+ throws IOException {
+ boolean bypassable = true;
+ boolean defaultResult = false;
+ if (coprocEnvironments.isEmpty()) {
+ return null;
+ }
+ return execOperationWithResult(
+ new ObserverOperationWithResult<RegionObserver,
Boolean>(regionObserverGetter,
+ defaultResult, bypassable) {
+ @Override
+ public Boolean call(RegionObserver observer) throws IOException {
+ return observer.preCheckAndPut(this, row, filter, put, getResult());
+ }
+ });
+ }
+
+ /**
+ * Supports Coprocessor 'bypass'.
+ * @param row row to check
* @param family column family
* @param qualifier column qualifier
* @param op the comparison operation
* @param comparator the comparator
* @param put data to put if check succeeds
* @return true or false to return to client if default processing should be
bypassed, or null
- * otherwise
+ * otherwise
*/
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_BOOLEAN_RETURN_NULL",
justification="Null is legit")
@@ -1112,6 +1138,33 @@ public class RegionCoprocessorHost
}
/**
+ * Supports Coprocessor 'bypass'.
+ * @param row row to check
+ * @param filter filter
+ * @param put data to put if check succeeds
+ * @return true or false to return to client if default processing should be
bypassed, or null
+ * otherwise
+ */
+
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_BOOLEAN_RETURN_NULL",
+ justification="Null is legit")
+ public Boolean preCheckAndPutAfterRowLock(
+ final byte[] row, final Filter filter, final Put put) throws IOException {
+ boolean bypassable = true;
+ boolean defaultResult = false;
+ if (coprocEnvironments.isEmpty()) {
+ return null;
+ }
+ return execOperationWithResult(
+ new ObserverOperationWithResult<RegionObserver,
Boolean>(regionObserverGetter,
+ defaultResult, bypassable) {
+ @Override
+ public Boolean call(RegionObserver observer) throws IOException {
+ return observer.preCheckAndPutAfterRowLock(this, row, filter, put,
getResult());
+ }
+ });
+ }
+
+ /**
* @param row row to check
* @param family column family
* @param qualifier column qualifier
@@ -1138,6 +1191,26 @@ public class RegionCoprocessorHost
}
/**
+ * @param row row to check
+ * @param filter filter
+ * @param put data to put if check succeeds
+ * @throws IOException e
+ */
+ public boolean postCheckAndPut(final byte [] row, final Filter filter, final
Put put,
+ boolean result) throws IOException {
+ if (this.coprocEnvironments.isEmpty()) {
+ return result;
+ }
+ return execOperationWithResult(
+ new ObserverOperationWithResult<RegionObserver,
Boolean>(regionObserverGetter, result) {
+ @Override
+ public Boolean call(RegionObserver observer) throws IOException {
+ return observer.postCheckAndPut(this, row, filter, put, getResult());
+ }
+ });
+ }
+
+ /**
* Supports Coprocessor 'bypass'.
* @param row row to check
* @param family column family
@@ -1145,8 +1218,8 @@ public class RegionCoprocessorHost
* @param op the comparison operation
* @param comparator the comparator
* @param delete delete to commit if check succeeds
- * @return true or false to return to client if default processing should be
bypassed,
- * or null otherwise
+ * @return true or false to return to client if default processing should be
bypassed, or null
+ * otherwise
*/
public Boolean preCheckAndDelete(final byte [] row, final byte [] family,
final byte [] qualifier, final CompareOperator op,
@@ -1171,6 +1244,31 @@ public class RegionCoprocessorHost
/**
* Supports Coprocessor 'bypass'.
* @param row row to check
+ * @param filter filter
+ * @param delete delete to commit if check succeeds
+ * @return true or false to return to client if default processing should be
bypassed, or null
+ * otherwise
+ */
+ public Boolean preCheckAndDelete(final byte [] row, final Filter filter,
final Delete delete)
+ throws IOException {
+ boolean bypassable = true;
+ boolean defaultResult = false;
+ if (coprocEnvironments.isEmpty()) {
+ return null;
+ }
+ return execOperationWithResult(
+ new ObserverOperationWithResult<RegionObserver,
Boolean>(regionObserverGetter,
+ defaultResult, bypassable) {
+ @Override
+ public Boolean call(RegionObserver observer) throws IOException {
+ return observer.preCheckAndDelete(this, row, filter, delete,
getResult());
+ }
+ });
+ }
+
+ /**
+ * Supports Coprocessor 'bypass'.
+ * @param row row to check
* @param family column family
* @param qualifier column qualifier
* @param op the comparison operation
@@ -1201,6 +1299,33 @@ public class RegionCoprocessorHost
}
/**
+ * Supports Coprocessor 'bypass'.
+ * @param row row to check
+ * @param filter filter
+ * @param delete delete to commit if check succeeds
+ * @return true or false to return to client if default processing should be
bypassed,
+ * or null otherwise
+ */
+
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_BOOLEAN_RETURN_NULL",
+ justification="Null is legit")
+ public Boolean preCheckAndDeleteAfterRowLock(final byte[] row, final Filter
filter,
+ final Delete delete) throws IOException {
+ boolean bypassable = true;
+ boolean defaultResult = false;
+ if (coprocEnvironments.isEmpty()) {
+ return null;
+ }
+ return execOperationWithResult(
+ new ObserverOperationWithResult<RegionObserver,
Boolean>(regionObserverGetter,
+ defaultResult, bypassable) {
+ @Override
+ public Boolean call(RegionObserver observer) throws IOException {
+ return observer.preCheckAndDeleteAfterRowLock(this, row, filter,
delete, getResult());
+ }
+ });
+ }
+
+ /**
* @param row row to check
* @param family column family
* @param qualifier column qualifier
@@ -1227,6 +1352,26 @@ public class RegionCoprocessorHost
}
/**
+ * @param row row to check
+ * @param filter filter
+ * @param delete delete to commit if check succeeds
+ * @throws IOException e
+ */
+ public boolean postCheckAndDelete(final byte [] row, final Filter filter,
final Delete delete,
+ boolean result) throws IOException {
+ if (this.coprocEnvironments.isEmpty()) {
+ return result;
+ }
+ return execOperationWithResult(
+ new ObserverOperationWithResult<RegionObserver,
Boolean>(regionObserverGetter, result) {
+ @Override
+ public Boolean call(RegionObserver observer) throws IOException {
+ return observer.postCheckAndDelete(this, row, filter, delete,
getResult());
+ }
+ });
+ }
+
+ /**
* Supports Coprocessor 'bypass'.
* @param append append object
* @return result to return to client if default operation should be
bypassed, null otherwise
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/DummyAsyncTable.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/DummyAsyncTable.java
index 2e9bb74..755d2a31 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/DummyAsyncTable.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/DummyAsyncTable.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.filter.Filter;
/**
* Can be overridden in UT if you only want to implement part of the methods
in {@link AsyncTable}.
@@ -106,6 +107,11 @@ public class DummyAsyncTable<C extends
ScanResultConsumerBase> implements AsyncT
}
@Override
+ public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ return null;
+ }
+
+ @Override
public CompletableFuture<Void> mutateRow(RowMutations mutation) {
return null;
}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTable.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTable.java
index 63080b9..b9fb811 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTable.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAsyncTable.java
@@ -30,6 +30,7 @@ import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@@ -41,10 +42,17 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import org.apache.commons.io.IOUtils;
+import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotEnabledException;
+import org.apache.hadoop.hbase.filter.BinaryComparator;
+import org.apache.hadoop.hbase.filter.FamilyFilter;
+import org.apache.hadoop.hbase.filter.FilterList;
+import org.apache.hadoop.hbase.filter.QualifierFilter;
+import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
+import org.apache.hadoop.hbase.filter.TimestampsFilter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.testclassification.ClientTests;
import org.apache.hadoop.hbase.testclassification.MediumTests;
@@ -402,6 +410,201 @@ public class TestAsyncTable {
}
@Test
+ public void testCheckAndMutateWithSingleFilter() throws Throwable {
+ AsyncTable<?> table = getTable.get();
+
+ // Put one row
+ Put put = new Put(row);
+ put.addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"));
+ put.addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b"));
+ put.addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c"));
+ table.put(put).get();
+
+ // Put with success
+ boolean ok = table.checkAndMutate(row, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("D"),
Bytes.toBytes("d")))
+ .get();
+ assertTrue(ok);
+
+ Result result = table.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D"))).get();
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(row, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("b")))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("E"),
Bytes.toBytes("e")))
+ .get();
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("E"))).get());
+
+ // Delete with success
+ ok = table.checkAndMutate(row, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .thenDelete(new Delete(row).addColumns(FAMILY, Bytes.toBytes("D")))
+ .get();
+ assertTrue(ok);
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D"))).get());
+
+ // Mutate with success
+ ok = table.checkAndMutate(row, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("B"),
+ CompareOperator.EQUAL, Bytes.toBytes("b")))
+ .thenMutate(new RowMutations(row)
+ .add((Mutation) new Put(row)
+ .addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")))
+ .add((Mutation) new Delete(row).addColumns(FAMILY,
Bytes.toBytes("A"))))
+ .get();
+ assertTrue(ok);
+
+ result = table.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D"))).get();
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("A"))).get());
+ }
+
+ @Test
+ public void testCheckAndMutateWithMultipleFilters() throws Throwable {
+ AsyncTable<?> table = getTable.get();
+
+ // Put one row
+ Put put = new Put(row);
+ put.addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"));
+ put.addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b"));
+ put.addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c"));
+ table.put(put).get();
+
+ // Put with success
+ boolean ok = table.checkAndMutate(row, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("D"),
Bytes.toBytes("d")))
+ .get();
+ assertTrue(ok);
+
+ Result result = table.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D"))).get();
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(row, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("c"))
+ ))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("E"),
Bytes.toBytes("e")))
+ .get();
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("E"))).get());
+
+ // Delete with success
+ ok = table.checkAndMutate(row, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ))
+ .thenDelete(new Delete(row).addColumns(FAMILY, Bytes.toBytes("D")))
+ .get();
+ assertTrue(ok);
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D"))).get());
+
+ // Mutate with success
+ ok = table.checkAndMutate(row, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ))
+ .thenMutate(new RowMutations(row)
+ .add((Mutation) new Put(row)
+ .addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")))
+ .add((Mutation) new Delete(row).addColumns(FAMILY,
Bytes.toBytes("A"))))
+ .get();
+ assertTrue(ok);
+
+ result = table.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D"))).get();
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("A"))).get());
+ }
+
+ @Test
+ public void testCheckAndMutateWithTimestampFilter() throws Throwable {
+ AsyncTable<?> table = getTable.get();
+
+ // Put with specifying the timestamp
+ table.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("A"), 100,
Bytes.toBytes("a"))).get();
+
+ // Put with success
+ boolean ok = table.checkAndMutate(row, new FilterList(
+ new FamilyFilter(CompareOperator.EQUAL, new BinaryComparator(FAMILY)),
+ new QualifierFilter(CompareOperator.EQUAL, new
BinaryComparator(Bytes.toBytes("A"))),
+ new TimestampsFilter(Collections.singletonList(100L))
+ ))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("B"),
Bytes.toBytes("b")))
+ .get();
+ assertTrue(ok);
+
+ Result result = table.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("B"))).get();
+ assertEquals("b", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("B"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(row, new FilterList(
+ new FamilyFilter(CompareOperator.EQUAL, new BinaryComparator(FAMILY)),
+ new QualifierFilter(CompareOperator.EQUAL, new
BinaryComparator(Bytes.toBytes("A"))),
+ new TimestampsFilter(Collections.singletonList(101L))
+ ))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("C"),
Bytes.toBytes("c")))
+ .get();
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("C"))).get());
+ }
+
+ @Test
+ public void testCheckAndMutateWithFilterAndTimeRange() throws Throwable {
+ AsyncTable<?> table = getTable.get();
+
+ // Put with specifying the timestamp
+ table.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("A"), 100,
Bytes.toBytes("a")))
+ .get();
+
+ // Put with success
+ boolean ok = table.checkAndMutate(row, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .timeRange(TimeRange.between(0, 101))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("B"),
Bytes.toBytes("b")))
+ .get();
+ assertTrue(ok);
+
+ Result result = table.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("B"))).get();
+ assertEquals("b", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("B"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(row, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .timeRange(TimeRange.between(0, 100))
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("C"),
Bytes.toBytes("c")))
+ .get();
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(row).addColumn(FAMILY,
Bytes.toBytes("C"))).get());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testCheckAndMutateWithNotSpecifyingCondition() throws Throwable {
+ getTable.get().checkAndMutate(row, FAMILY)
+ .thenPut(new Put(row).addColumn(FAMILY, Bytes.toBytes("D"),
Bytes.toBytes("d")));
+ }
+
+ @Test
public void testDisabled() throws InterruptedException, ExecutionException {
ASYNC_CONN.getAdmin().disableTable(TABLE_NAME).get();
try {
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestCheckAndMutate.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestCheckAndMutate.java
index 53353d2..f399e86 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestCheckAndMutate.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestCheckAndMutate.java
@@ -18,14 +18,25 @@
package org.apache.hadoop.hbase.client;
import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
+import java.util.Collections;
+import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.filter.BinaryComparator;
+import org.apache.hadoop.hbase.filter.FamilyFilter;
+import org.apache.hadoop.hbase.filter.FilterList;
+import org.apache.hadoop.hbase.filter.QualifierFilter;
+import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
+import org.apache.hadoop.hbase.filter.TimestampsFilter;
+import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
import org.apache.hadoop.hbase.testclassification.MediumTests;
import org.apache.hadoop.hbase.util.Bytes;
@@ -182,4 +193,183 @@ public class TestCheckAndMutate {
}
}
+ @Test
+ public void testCheckAndMutateWithSingleFilter() throws Throwable {
+ try (Table table = createTable()) {
+ // put one row
+ putOneRow(table);
+ // get row back and assert the values
+ getOneRowAndAssertAllExist(table);
+
+ // Put with success
+ boolean ok = table.checkAndMutate(ROWKEY, new
SingleColumnValueFilter(FAMILY,
+ Bytes.toBytes("A"), CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("D"),
Bytes.toBytes("d")));
+ assertTrue(ok);
+
+ Result result = table.get(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("D")));
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(ROWKEY, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("b")))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("E"),
Bytes.toBytes("e")));
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("E"))));
+
+ // Delete with success
+ ok = table.checkAndMutate(ROWKEY, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .thenDelete(new Delete(ROWKEY).addColumns(FAMILY, Bytes.toBytes("D")));
+ assertTrue(ok);
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("D"))));
+
+ // Mutate with success
+ ok = table.checkAndMutate(ROWKEY, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("B"),
+ CompareOperator.EQUAL, Bytes.toBytes("b")))
+ .thenMutate(new RowMutations(ROWKEY)
+ .add((Mutation) new Put(ROWKEY)
+ .addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")))
+ .add((Mutation) new Delete(ROWKEY).addColumns(FAMILY,
Bytes.toBytes("A"))));
+ assertTrue(ok);
+
+ result = table.get(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("D")));
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("A"))));
+ }
+ }
+
+ @Test
+ public void testCheckAndMutateWithMultipleFilters() throws Throwable {
+ try (Table table = createTable()) {
+ // put one row
+ putOneRow(table);
+ // get row back and assert the values
+ getOneRowAndAssertAllExist(table);
+
+ // Put with success
+ boolean ok = table.checkAndMutate(ROWKEY, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("D"),
Bytes.toBytes("d")));
+ assertTrue(ok);
+
+ Result result = table.get(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("D")));
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(ROWKEY, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("c"))
+ ))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("E"),
Bytes.toBytes("e")));
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("E"))));
+
+ // Delete with success
+ ok = table.checkAndMutate(ROWKEY, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ))
+ .thenDelete(new Delete(ROWKEY).addColumns(FAMILY, Bytes.toBytes("D")));
+ assertTrue(ok);
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("D"))));
+
+ // Mutate with success
+ ok = table.checkAndMutate(ROWKEY, new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ))
+ .thenMutate(new RowMutations(ROWKEY)
+ .add((Mutation) new Put(ROWKEY)
+ .addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")))
+ .add((Mutation) new Delete(ROWKEY).addColumns(FAMILY,
Bytes.toBytes("A"))));
+ assertTrue(ok);
+
+ result = table.get(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("D")));
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("A"))));
+ }
+ }
+
+ @Test
+ public void testCheckAndMutateWithTimestampFilter() throws Throwable {
+ try (Table table = createTable()) {
+ // Put with specifying the timestamp
+ table.put(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("A"), 100,
Bytes.toBytes("a")));
+
+ // Put with success
+ boolean ok = table.checkAndMutate(ROWKEY, new FilterList(
+ new FamilyFilter(CompareOperator.EQUAL, new
BinaryComparator(FAMILY)),
+ new QualifierFilter(CompareOperator.EQUAL, new
BinaryComparator(Bytes.toBytes("A"))),
+ new TimestampsFilter(Collections.singletonList(100L))
+ ))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("B"),
Bytes.toBytes("b")));
+ assertTrue(ok);
+
+ Result result = table.get(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("B")));
+ assertEquals("b", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("B"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(ROWKEY, new FilterList(
+ new FamilyFilter(CompareOperator.EQUAL, new
BinaryComparator(FAMILY)),
+ new QualifierFilter(CompareOperator.EQUAL, new
BinaryComparator(Bytes.toBytes("A"))),
+ new TimestampsFilter(Collections.singletonList(101L))
+ ))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("C"),
Bytes.toBytes("c")));
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("C"))));
+ }
+ }
+
+ @Test
+ public void testCheckAndMutateWithFilterAndTimeRange() throws Throwable {
+ try (Table table = createTable()) {
+ // Put with specifying the timestamp
+ table.put(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("A"), 100,
Bytes.toBytes("a")));
+
+ // Put with success
+ boolean ok = table.checkAndMutate(ROWKEY, new
SingleColumnValueFilter(FAMILY,
+ Bytes.toBytes("A"), CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .timeRange(TimeRange.between(0, 101))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("B"),
Bytes.toBytes("b")));
+ assertTrue(ok);
+
+ Result result = table.get(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("B")));
+ assertEquals("b", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("B"))));
+
+ // Put with failure
+ ok = table.checkAndMutate(ROWKEY, new SingleColumnValueFilter(FAMILY,
Bytes.toBytes("A"),
+ CompareOperator.EQUAL, Bytes.toBytes("a")))
+ .timeRange(TimeRange.between(0, 100))
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("C"),
Bytes.toBytes("c")));
+ assertFalse(ok);
+
+ assertFalse(table.exists(new Get(ROWKEY).addColumn(FAMILY,
Bytes.toBytes("C"))));
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testCheckAndMutateWithNotSpecifyingCondition() throws Throwable {
+ try (Table table = createTable()) {
+ table.checkAndMutate(ROWKEY, FAMILY)
+ .thenPut(new Put(ROWKEY).addColumn(FAMILY, Bytes.toBytes("D"),
Bytes.toBytes("d")));
+ }
+ }
}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMalformedCellFromClient.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMalformedCellFromClient.java
index ab7d070..655225a 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMalformedCellFromClient.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestMalformedCellFromClient.java
@@ -30,12 +30,12 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.DoNotRetryIOException;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.TableName;
-import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.HRegionServer;
@@ -234,8 +234,7 @@ public class TestMalformedCellFromClient {
ClientProtos.Action.Builder actionBuilder =
ClientProtos.Action.newBuilder();
ClientProtos.MutationProto.Builder mutationBuilder =
ClientProtos.MutationProto.newBuilder();
ClientProtos.Condition condition = RequestConverter
- .buildCondition(rm.getRow(), FAMILY, null, new BinaryComparator(new
byte[10]),
- HBaseProtos.CompareType.EQUAL, null);
+ .buildCondition(rm.getRow(), FAMILY, null, CompareOperator.EQUAL, new
byte[10], null, null);
for (Mutation mutation : rm.getMutations()) {
ClientProtos.MutationProto.MutationType mutateType = null;
if (mutation instanceof Put) {
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java
index caf0abb..523466d 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java
@@ -49,6 +49,7 @@ import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.ByteArrayComparable;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
import org.apache.hadoop.hbase.io.Reference;
import org.apache.hadoop.hbase.io.hfile.CacheConfig;
@@ -99,11 +100,17 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
final AtomicInteger ctPostIncrement = new AtomicInteger(0);
final AtomicInteger ctPostAppend = new AtomicInteger(0);
final AtomicInteger ctPreCheckAndPut = new AtomicInteger(0);
+ final AtomicInteger ctPreCheckAndPutWithFilter = new AtomicInteger(0);
final AtomicInteger ctPreCheckAndPutAfterRowLock = new AtomicInteger(0);
+ final AtomicInteger ctPreCheckAndPutWithFilterAfterRowLock = new
AtomicInteger(0);
final AtomicInteger ctPostCheckAndPut = new AtomicInteger(0);
+ final AtomicInteger ctPostCheckAndPutWithFilter = new AtomicInteger(0);
final AtomicInteger ctPreCheckAndDelete = new AtomicInteger(0);
+ final AtomicInteger ctPreCheckAndDeleteWithFilter = new AtomicInteger(0);
final AtomicInteger ctPreCheckAndDeleteAfterRowLock = new AtomicInteger(0);
+ final AtomicInteger ctPreCheckAndDeleteWithFilterAfterRowLock = new
AtomicInteger(0);
final AtomicInteger ctPostCheckAndDelete = new AtomicInteger(0);
+ final AtomicInteger ctPostCheckAndDeleteWithFilter = new AtomicInteger(0);
final AtomicInteger ctPreScannerNext = new AtomicInteger(0);
final AtomicInteger ctPostScannerNext = new AtomicInteger(0);
final AtomicInteger ctPostScannerFilterRow = new AtomicInteger(0);
@@ -493,6 +500,13 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
}
@Override
+ public boolean preCheckAndPut(ObserverContext<RegionCoprocessorEnvironment>
c, byte[] row,
+ Filter filter, Put put, boolean result) throws IOException {
+ ctPreCheckAndPutWithFilter.incrementAndGet();
+ return true;
+ }
+
+ @Override
public boolean
preCheckAndPutAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> e,
byte[] row, byte[] family, byte[] qualifier, CompareOperator compareOp,
ByteArrayComparable comparator, Put put, boolean result) throws
IOException {
@@ -501,6 +515,13 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
}
@Override
+ public boolean
preCheckAndPutAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> c,
+ byte[] row, Filter filter, Put put, boolean result) throws IOException {
+ ctPreCheckAndPutWithFilterAfterRowLock.incrementAndGet();
+ return true;
+ }
+
+ @Override
public boolean postCheckAndPut(ObserverContext<RegionCoprocessorEnvironment>
e, byte[] row,
byte[] family, byte[] qualifier,
CompareOperator compareOp, ByteArrayComparable comparator,
Put put, boolean result) throws IOException {
@@ -509,6 +530,13 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
}
@Override
+ public boolean postCheckAndPut(ObserverContext<RegionCoprocessorEnvironment>
c, byte[] row,
+ Filter filter, Put put, boolean result) throws IOException {
+ ctPostCheckAndPutWithFilter.incrementAndGet();
+ return true;
+ }
+
+ @Override
public boolean
preCheckAndDelete(ObserverContext<RegionCoprocessorEnvironment> e, byte[] row,
byte[] family, byte[] qualifier,
CompareOperator compareOp, ByteArrayComparable comparator,
Delete delete, boolean result) throws
IOException {
@@ -517,6 +545,13 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
}
@Override
+ public boolean
preCheckAndDelete(ObserverContext<RegionCoprocessorEnvironment> c, byte[] row,
+ Filter filter, Delete delete, boolean result) throws IOException {
+ ctPreCheckAndDeleteWithFilter.incrementAndGet();
+ return true;
+ }
+
+ @Override
public boolean
preCheckAndDeleteAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> e,
byte[] row, byte[] family, byte[] qualifier, CompareOperator compareOp,
ByteArrayComparable comparator, Delete delete, boolean result) throws
IOException {
@@ -525,6 +560,13 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
}
@Override
+ public boolean
preCheckAndDeleteAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> c,
+ byte[] row, Filter filter, Delete delete, boolean result) throws
IOException {
+ ctPreCheckAndDeleteWithFilterAfterRowLock.incrementAndGet();
+ return true;
+ }
+
+ @Override
public boolean
postCheckAndDelete(ObserverContext<RegionCoprocessorEnvironment> e, byte[] row,
byte[] family, byte[] qualifier,
CompareOperator compareOp, ByteArrayComparable comparator,
Delete delete, boolean result) throws
IOException {
@@ -533,6 +575,13 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
}
@Override
+ public boolean
postCheckAndDelete(ObserverContext<RegionCoprocessorEnvironment> e, byte[] row,
+ Filter filter, Delete delete, boolean result) throws IOException {
+ ctPostCheckAndDeleteWithFilter.incrementAndGet();
+ return true;
+ }
+
+ @Override
public Result
preAppendAfterRowLock(ObserverContext<RegionCoprocessorEnvironment> e,
Append append) throws IOException {
ctPreAppendAfterRowLock.incrementAndGet();
@@ -693,28 +742,52 @@ public class SimpleRegionObserver implements
RegionCoprocessor, RegionObserver {
return ctPostCloseRegionOperation.get();
}
- public boolean hadPreCheckAndPut() {
- return ctPreCheckAndPut.get() > 0;
+ public int getPreCheckAndPut() {
+ return ctPreCheckAndPut.get();
+ }
+
+ public int getPreCheckAndPutWithFilter() {
+ return ctPreCheckAndPutWithFilter.get();
+ }
+
+ public int getPreCheckAndPutAfterRowLock() {
+ return ctPreCheckAndPutAfterRowLock.get();
+ }
+
+ public int getPreCheckAndPutWithFilterAfterRowLock() {
+ return ctPreCheckAndPutWithFilterAfterRowLock.get();
+ }
+
+ public int getPostCheckAndPut() {
+ return ctPostCheckAndPut.get();
+ }
+
+ public int getPostCheckAndPutWithFilter() {
+ return ctPostCheckAndPutWithFilter.get();
+ }
+
+ public int getPreCheckAndDelete() {
+ return ctPreCheckAndDelete.get();
}
- public boolean hadPreCheckAndPutAfterRowLock() {
- return ctPreCheckAndPutAfterRowLock.get() > 0;
+ public int getPreCheckAndDeleteWithFilter() {
+ return ctPreCheckAndDeleteWithFilter.get();
}
- public boolean hadPostCheckAndPut() {
- return ctPostCheckAndPut.get() > 0;
+ public int getPreCheckAndDeleteAfterRowLock() {
+ return ctPreCheckAndDeleteAfterRowLock.get();
}
- public boolean hadPreCheckAndDelete() {
- return ctPreCheckAndDelete.get() > 0;
+ public int getPreCheckAndDeleteWithFilterAfterRowLock() {
+ return ctPreCheckAndDeleteWithFilterAfterRowLock.get();
}
- public boolean hadPreCheckAndDeleteAfterRowLock() {
- return ctPreCheckAndDeleteAfterRowLock.get() > 0;
+ public int getPostCheckAndDelete() {
+ return ctPostCheckAndDelete.get();
}
- public boolean hadPostCheckAndDelete() {
- return ctPostCheckAndDelete.get() > 0;
+ public int getPostCheckAndDeleteWithFilter() {
+ return ctPostCheckAndDeleteWithFilter.get();
}
public boolean hadPreIncrement() {
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java
index 9f76649..e9a354a 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java
@@ -33,6 +33,7 @@ import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.Coprocessor;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
@@ -60,6 +61,7 @@ import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
import org.apache.hadoop.hbase.filter.FilterAllFilter;
+import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.io.hfile.CacheConfig;
import org.apache.hadoop.hbase.io.hfile.HFile;
import org.apache.hadoop.hbase.io.hfile.HFileContext;
@@ -263,12 +265,26 @@ public class TestRegionObserverInterface {
p = new Put(Bytes.toBytes(0));
p.addColumn(A, A, A);
verifyMethodResult(SimpleRegionObserver.class,
- new String[] { "hadPreCheckAndPut", "hadPreCheckAndPutAfterRowLock",
"hadPostCheckAndPut" },
- tableName, new Boolean[] { false, false, false });
+ new String[] { "getPreCheckAndPut", "getPreCheckAndPutAfterRowLock",
"getPostCheckAndPut",
+ "getPreCheckAndPutWithFilter",
"getPreCheckAndPutWithFilterAfterRowLock",
+ "getPostCheckAndPutWithFilter" },
+ tableName, new Integer[] { 0, 0, 0, 0, 0, 0 });
+
table.checkAndMutate(Bytes.toBytes(0),
A).qualifier(A).ifEquals(A).thenPut(p);
verifyMethodResult(SimpleRegionObserver.class,
- new String[] { "hadPreCheckAndPut", "hadPreCheckAndPutAfterRowLock",
"hadPostCheckAndPut" },
- tableName, new Boolean[] { true, true, true });
+ new String[] { "getPreCheckAndPut", "getPreCheckAndPutAfterRowLock",
"getPostCheckAndPut",
+ "getPreCheckAndPutWithFilter",
"getPreCheckAndPutWithFilterAfterRowLock",
+ "getPostCheckAndPutWithFilter" },
+ tableName, new Integer[] { 1, 1, 1, 0, 0, 0 });
+
+ table.checkAndMutate(Bytes.toBytes(0),
+ new SingleColumnValueFilter(A, A, CompareOperator.EQUAL, A))
+ .thenPut(p);
+ verifyMethodResult(SimpleRegionObserver.class,
+ new String[] { "getPreCheckAndPut", "getPreCheckAndPutAfterRowLock",
"getPostCheckAndPut",
+ "getPreCheckAndPutWithFilter",
"getPreCheckAndPutWithFilterAfterRowLock",
+ "getPostCheckAndPutWithFilter" },
+ tableName, new Integer[] { 1, 1, 1, 1, 1, 1 });
} finally {
util.deleteTable(tableName);
}
@@ -285,14 +301,29 @@ public class TestRegionObserverInterface {
Delete d = new Delete(Bytes.toBytes(0));
table.delete(d);
verifyMethodResult(
- SimpleRegionObserver.class, new String[] { "hadPreCheckAndDelete",
- "hadPreCheckAndDeleteAfterRowLock", "hadPostCheckAndDelete" },
- tableName, new Boolean[] { false, false, false });
+ SimpleRegionObserver.class, new String[] { "getPreCheckAndDelete",
+ "getPreCheckAndDeleteAfterRowLock", "getPostCheckAndDelete",
+ "getPreCheckAndDeleteWithFilter",
"getPreCheckAndDeleteWithFilterAfterRowLock",
+ "getPostCheckAndDeleteWithFilter" },
+ tableName, new Integer[] { 0, 0, 0, 0, 0, 0 });
+
table.checkAndMutate(Bytes.toBytes(0),
A).qualifier(A).ifEquals(A).thenDelete(d);
verifyMethodResult(
- SimpleRegionObserver.class, new String[] { "hadPreCheckAndDelete",
- "hadPreCheckAndDeleteAfterRowLock", "hadPostCheckAndDelete" },
- tableName, new Boolean[] { true, true, true });
+ SimpleRegionObserver.class, new String[] { "getPreCheckAndDelete",
+ "getPreCheckAndDeleteAfterRowLock", "getPostCheckAndDelete",
+ "getPreCheckAndDeleteWithFilter",
"getPreCheckAndDeleteWithFilterAfterRowLock",
+ "getPostCheckAndDeleteWithFilter" },
+ tableName, new Integer[] { 1, 1, 1, 0, 0, 0 });
+
+ table.checkAndMutate(Bytes.toBytes(0),
+ new SingleColumnValueFilter(A, A, CompareOperator.EQUAL, A))
+ .thenDelete(d);
+ verifyMethodResult(
+ SimpleRegionObserver.class, new String[] { "getPreCheckAndDelete",
+ "getPreCheckAndDeleteAfterRowLock", "getPostCheckAndDelete",
+ "getPreCheckAndDeleteWithFilter",
"getPreCheckAndDeleteWithFilterAfterRowLock",
+ "getPostCheckAndDeleteWithFilter" },
+ tableName, new Integer[] { 1, 1, 1, 1, 1, 1 });
} finally {
util.deleteTable(tableName);
table.close();
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/RegionAsTable.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/RegionAsTable.java
index 07b834b..27a8406 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/RegionAsTable.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/RegionAsTable.java
@@ -47,6 +47,7 @@ import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.coprocessor.Batch.Call;
import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
/**
@@ -219,6 +220,11 @@ public class RegionAsTable implements Table {
}
@Override
+ public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void mutateRow(RowMutations rm) throws IOException {
throw new UnsupportedOperationException();
}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java
index 53ec7d1..6b4dfcf 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java
@@ -129,6 +129,7 @@ import
org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.filter.ValueFilter;
+import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.io.hfile.HFile;
import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
import org.apache.hadoop.hbase.monitoring.MonitoredTask;
@@ -2147,6 +2148,128 @@ public class TestHRegion {
assertEquals(0, r.size());
}
+ @Test
+ public void testCheckAndMutate_WithFilters() throws Throwable {
+ final byte[] FAMILY = Bytes.toBytes("fam");
+
+ // Setting up region
+ this.region = initHRegion(tableName, method, CONF, FAMILY);
+
+ // Put one row
+ Put put = new Put(row);
+ put.addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"));
+ put.addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b"));
+ put.addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c"));
+ region.put(put);
+
+ // Put with success
+ boolean ok = region.checkAndMutate(row,
+ new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ),
+ new Put(row).addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")));
+ assertTrue(ok);
+
+ Result result = region.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D")));
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ // Put with failure
+ ok = region.checkAndMutate(row,
+ new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("c"))
+ ),
+ new Put(row).addColumn(FAMILY, Bytes.toBytes("E"), Bytes.toBytes("e")));
+ assertFalse(ok);
+
+ assertTrue(region.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("E"))).isEmpty());
+
+ // Delete with success
+ ok = region.checkAndMutate(row,
+ new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ),
+ new Delete(row).addColumns(FAMILY, Bytes.toBytes("D")));
+ assertTrue(ok);
+
+ assertTrue(region.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("D"))).isEmpty());
+
+ // Mutate with success
+ ok = region.checkAndRowMutate(row,
+ new FilterList(
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("B"),
CompareOperator.EQUAL,
+ Bytes.toBytes("b"))
+ ),
+ new RowMutations(row)
+ .add((Mutation) new Put(row)
+ .addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")))
+ .add((Mutation) new Delete(row).addColumns(FAMILY,
Bytes.toBytes("A"))));
+ assertTrue(ok);
+
+ result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D")));
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ assertTrue(region.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("A"))).isEmpty());
+ }
+
+ @Test
+ public void testCheckAndMutate_WithFiltersAndTimeRange() throws Throwable {
+ final byte[] FAMILY = Bytes.toBytes("fam");
+
+ // Setting up region
+ this.region = initHRegion(tableName, method, CONF, FAMILY);
+
+ // Put with specifying the timestamp
+ region.put(new Put(row).addColumn(FAMILY, Bytes.toBytes("A"), 100,
Bytes.toBytes("a")));
+
+ // Put with success
+ boolean ok = region.checkAndMutate(row,
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ TimeRange.between(0, 101),
+ new Put(row).addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b")));
+ assertTrue(ok);
+
+ Result result = region.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("B")));
+ assertEquals("b", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("B"))));
+
+ // Put with failure
+ ok = region.checkAndMutate(row,
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ TimeRange.between(0, 100),
+ new Put(row).addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c")));
+ assertFalse(ok);
+
+ assertTrue(region.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("C"))).isEmpty());
+
+ // Mutate with success
+ ok = region.checkAndRowMutate(row,
+ new SingleColumnValueFilter(FAMILY, Bytes.toBytes("A"),
CompareOperator.EQUAL,
+ Bytes.toBytes("a")),
+ TimeRange.between(0, 101),
+ new RowMutations(row)
+ .add((Mutation) new Put(row)
+ .addColumn(FAMILY, Bytes.toBytes("D"), Bytes.toBytes("d")))
+ .add((Mutation) new Delete(row).addColumns(FAMILY,
Bytes.toBytes("A"))));
+ assertTrue(ok);
+
+ result = region.get(new Get(row).addColumn(FAMILY, Bytes.toBytes("D")));
+ assertEquals("d", Bytes.toString(result.getValue(FAMILY,
Bytes.toBytes("D"))));
+
+ assertTrue(region.get(new Get(row).addColumn(FAMILY,
Bytes.toBytes("A"))).isEmpty());
+ }
+
//
////////////////////////////////////////////////////////////////////////////
// Delete tests
//
////////////////////////////////////////////////////////////////////////////
diff --git
a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftTable.java
b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftTable.java
index 2bae685..30b1fa1 100644
---
a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftTable.java
+++
b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift2/client/ThriftTable.java
@@ -49,6 +49,7 @@ import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.coprocessor.Batch;
import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
+import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.TimeRange;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.thrift2.ThriftUtilities;
@@ -426,6 +427,11 @@ public class ThriftTable implements Table {
}
@Override
+ public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter
filter) {
+ throw new NotImplementedException("Implement later");
+ }
+
+ @Override
public void mutateRow(RowMutations rm) throws IOException {
TRowMutations tRowMutations = ThriftUtilities.rowMutationsFromHBase(rm);
try {