diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTable.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTable.java index ae73c514539..908f44ac0d1 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTable.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTable.java @@ -53,6 +53,7 @@ import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.coprocessor.Batch; import org.apache.hadoop.hbase.filter.BinaryComparator; +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController; import org.apache.hadoop.hbase.ipc.RegionCoprocessorRpcChannel; @@ -1148,6 +1149,31 @@ public class HTable implements HTableInterface { return rpcCallerFactory. newCaller().callWithRetries(callable, this.operationTimeout); } + /** + * {@inheritDoc} + */ + @Override + public boolean checkAndPut(final byte [] row, final byte [] family, + final byte [] qualifier, final CompareOp compareOp, final byte [] value, + final Put put) + throws IOException { + RegionServerCallable callable = + new RegionServerCallable(connection, getName(), row) { + public Boolean call() throws IOException { + try { + CompareType compareType = CompareType.valueOf(compareOp.name()); + MutateRequest request = RequestConverter.buildMutateRequest( + getLocation().getRegionInfo().getRegionName(), row, family, qualifier, + new BinaryComparator(value), compareType, put); + MutateResponse response = getStub().mutate(null, request); + return Boolean.valueOf(response.getProcessed()); + } catch (ServiceException se) { + throw ProtobufUtil.getRemoteException(se); + } + } + }; + return rpcCallerFactory. newCaller().callWithRetries(callable, this.operationTimeout); + } /** * {@inheritDoc} @@ -1174,6 +1200,32 @@ public class HTable implements HTableInterface { return rpcCallerFactory. newCaller().callWithRetries(callable, this.operationTimeout); } + /** + * {@inheritDoc} + */ + @Override + public boolean checkAndDelete(final byte [] row, final byte [] family, + final byte [] qualifier, final CompareOp compareOp, final byte [] value, + final Delete delete) + throws IOException { + RegionServerCallable callable = + new RegionServerCallable(connection, getName(), row) { + public Boolean call() throws IOException { + try { + CompareType compareType = CompareType.valueOf(compareOp.name()); + MutateRequest request = RequestConverter.buildMutateRequest( + getLocation().getRegionInfo().getRegionName(), row, family, qualifier, + new BinaryComparator(value), compareType, delete); + MutateResponse response = getStub().mutate(null, request); + return Boolean.valueOf(response.getProcessed()); + } catch (ServiceException se) { + throw ProtobufUtil.getRemoteException(se); + } + } + }; + return rpcCallerFactory. newCaller().callWithRetries(callable, this.operationTimeout); + } + /** * {@inheritDoc} */ diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java index ab34d5b64e6..d7126f07610 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTableInterface.java @@ -28,6 +28,7 @@ import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.client.coprocessor.Batch; +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; import java.io.Closeable; @@ -280,6 +281,8 @@ public interface HTableInterface extends Closeable { boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, byte[] value, Put put) throws IOException; + boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Put put) throws IOException; /** * Deletes the specified cells/row. * @@ -318,6 +321,9 @@ public interface HTableInterface extends Closeable { boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, byte[] value, Delete delete) throws IOException; + boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Delete delete) throws IOException; + /** * Performs multiple mutations atomically on a single row. Currently * {@link Put} and {@link Delete} are supported. diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java index e0e4e32b262..ba3d76d46f1 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java @@ -31,6 +31,8 @@ import org.apache.hadoop.hbase.HTableDescriptor; 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.BinaryComparator; +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.PoolMap; @@ -445,6 +447,13 @@ public class HTablePool implements Closeable { return table.checkAndPut(row, family, qualifier, value, put); } + @Override + public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Put put) throws IOException { + checkState(); + return table.checkAndPut(row, family, qualifier, compareOp, value, put); + } + @Override public void delete(Delete delete) throws IOException { checkState(); @@ -464,6 +473,13 @@ public class HTablePool implements Closeable { return table.checkAndDelete(row, family, qualifier, value, delete); } + @Override + public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Delete delete) throws IOException { + checkState(); + return table.checkAndDelete(row, family, qualifier, compareOp, value, delete); + } + @Override public Result increment(Increment increment) throws IOException { checkState(); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java index 78db541ffb8..32c83ad8c36 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java @@ -62,6 +62,7 @@ import org.apache.hadoop.hbase.client.Row; import org.apache.hadoop.hbase.client.RowMutations; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.coprocessor.Batch; +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.CoprocessorClassLoader; @@ -453,11 +454,21 @@ public abstract class CoprocessorHost { return table.checkAndPut(row, family, qualifier, value, put); } + public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Put put) throws IOException { + return table.checkAndPut(row, family, qualifier, compareOp, value, put); + } + public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, byte[] value, Delete delete) throws IOException { return table.checkAndDelete(row, family, qualifier, value, delete); } + public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Delete delete) throws IOException { + return table.checkAndDelete(row, family, qualifier, compareOp, value, delete); + } + public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount) throws IOException { return table.incrementColumnValue(row, family, qualifier, amount); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java index 6352c4a463c..0e17362a3eb 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java @@ -53,6 +53,7 @@ import org.apache.hadoop.hbase.client.Row; import org.apache.hadoop.hbase.client.RowMutations; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.coprocessor.Batch; +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.io.TimeRange; import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; import org.apache.hadoop.hbase.rest.Constants; @@ -668,6 +669,11 @@ public class RemoteHTable implements HTableInterface { throw new IOException("checkAndPut request timed out"); } + public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Put put) throws IOException { + throw new IOException("checkAndPut for non-equal comparison not implemented"); + } + public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, byte[] value, Delete delete) throws IOException { Put put = new Put(row); @@ -703,6 +709,11 @@ public class RemoteHTable implements HTableInterface { throw new IOException("checkAndDelete request timed out"); } + public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, + CompareOp compareOp, byte[] value, Delete delete) throws IOException { + throw new IOException("checkAndDelete for non-equal comparison not implemented"); + } + public Result increment(Increment increment) throws IOException { throw new IOException("Increment not supported"); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java index 4bfeccd7f04..792504e54d8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java @@ -4775,6 +4775,146 @@ public class TestFromClientSide { } + @Test + public void testCheckAndPutWithCompareOp() throws IOException { + final byte [] value1 = Bytes.toBytes("aaaa"); + final byte [] value2 = Bytes.toBytes("bbbb"); + final byte [] value3 = Bytes.toBytes("cccc"); + final byte [] value4 = Bytes.toBytes("dddd"); + + HTable table = TEST_UTIL.createTable(Bytes.toBytes("testCheckAndPutWithCompareOp"), + new byte [][] {FAMILY}); + + Put put2 = new Put(ROW); + put2.add(FAMILY, QUALIFIER, value2); + + Put put3 = new Put(ROW); + put3.add(FAMILY, QUALIFIER, value3); + + // row doesn't exist, so using "null" to check for existence should be considered "match". + boolean ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, null, put2); + assertEquals(ok, true); + + // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL + // turns out "match" + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.GREATER, value1, put2); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.EQUAL, value1, put2); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.GREATER_OR_EQUAL, value1, put2); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.LESS, value1, put2); + assertEquals(ok, true); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.LESS_OR_EQUAL, value1, put2); + assertEquals(ok, true); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.NOT_EQUAL, value1, put3); + assertEquals(ok, true); + + // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL + // turns out "match" + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.LESS, value4, put3); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.LESS_OR_EQUAL, value4, put3); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.EQUAL, value4, put3); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.GREATER, value4, put3); + assertEquals(ok, true); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.GREATER_OR_EQUAL, value4, put3); + assertEquals(ok, true); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.NOT_EQUAL, value4, put2); + assertEquals(ok, true); + + // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL + // turns out "match" + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.GREATER, value2, put2); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.NOT_EQUAL, value2, put2); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.LESS, value2, put2); + assertEquals(ok, false); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.GREATER_OR_EQUAL, value2, put2); + assertEquals(ok, true); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.LESS_OR_EQUAL, value2, put2); + assertEquals(ok, true); + ok = table.checkAndPut(ROW, FAMILY, QUALIFIER, CompareOp.EQUAL, value2, put3); + assertEquals(ok, true); + } + + @Test + public void testCheckAndDeleteWithCompareOp() throws IOException { + final byte [] value1 = Bytes.toBytes("aaaa"); + final byte [] value2 = Bytes.toBytes("bbbb"); + final byte [] value3 = Bytes.toBytes("cccc"); + final byte [] value4 = Bytes.toBytes("dddd"); + + HTable table = TEST_UTIL.createTable(Bytes.toBytes("testCheckAndDeleteWithCompareOp"), + new byte [][] {FAMILY}); + + Put put2 = new Put(ROW); + put2.add(FAMILY, QUALIFIER, value2); + table.put(put2); + + Put put3 = new Put(ROW); + put3.add(FAMILY, QUALIFIER, value3); + + Delete delete = new Delete(ROW); + delete.deleteColumns(FAMILY, QUALIFIER); + + // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL + // turns out "match" + boolean ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.GREATER, value1, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.EQUAL, value1, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.GREATER_OR_EQUAL, value1, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.LESS, value1, delete); + assertEquals(ok, true); + table.put(put2); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.LESS_OR_EQUAL, value1, delete); + assertEquals(ok, true); + table.put(put2); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.NOT_EQUAL, value1, delete); + assertEquals(ok, true); + + // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL + // turns out "match" + table.put(put3); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.LESS, value4, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.LESS_OR_EQUAL, value4, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.EQUAL, value4, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.GREATER, value4, delete); + assertEquals(ok, true); + table.put(put3); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.GREATER_OR_EQUAL, value4, delete); + assertEquals(ok, true); + table.put(put3); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.NOT_EQUAL, value4, delete); + assertEquals(ok, true); + + // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL + // turns out "match" + table.put(put2); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.GREATER, value2, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.NOT_EQUAL, value2, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.LESS, value2, delete); + assertEquals(ok, false); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.GREATER_OR_EQUAL, value2, delete); + assertEquals(ok, true); + table.put(put2); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.LESS_OR_EQUAL, value2, delete); + assertEquals(ok, true); + table.put(put2); + ok = table.checkAndDelete(ROW, FAMILY, QUALIFIER, CompareOp.EQUAL, value2, delete); + assertEquals(ok, true); + } + /** * Test ScanMetrics * @throws Exception