001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.junit.jupiter.api.Assertions.assertFalse;
021import static org.junit.jupiter.api.Assertions.assertTrue;
022import static org.junit.jupiter.api.Assertions.fail;
023
024import java.io.IOException;
025import org.apache.hadoop.hbase.CompareOperator;
026import org.apache.hadoop.hbase.io.TimeRange;
027import org.apache.hadoop.hbase.util.Bytes;
028import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
029import org.junit.jupiter.api.TestTemplate;
030
031@SuppressWarnings("deprecation")
032public class FromClientSideTestCheckAndMutate extends FromClientSideTestBase {
033
034  protected FromClientSideTestCheckAndMutate(Class<? extends ConnectionRegistry> registryImpl,
035    int numHedgedReqs) {
036    super(registryImpl, numHedgedReqs);
037  }
038
039  @TestTemplate
040  public void testCheckAndPut() throws IOException {
041    final byte[] anotherrow = Bytes.toBytes("anotherrow");
042    final byte[] value2 = Bytes.toBytes("abcd");
043    TEST_UTIL.createTable(tableName, FAMILY);
044    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
045      Put put1 = new Put(ROW);
046      put1.addColumn(FAMILY, QUALIFIER, VALUE);
047
048      // row doesn't exist, so using non-null value should be considered "not match".
049      boolean ok =
050        table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(VALUE).thenPut(put1);
051      assertFalse(ok);
052
053      // row doesn't exist, so using "ifNotExists" should be considered "match".
054      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put1);
055      assertTrue(ok);
056
057      // row now exists, so using "ifNotExists" should be considered "not match".
058      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put1);
059      assertFalse(ok);
060
061      Put put2 = new Put(ROW);
062      put2.addColumn(FAMILY, QUALIFIER, value2);
063
064      // row now exists, use the matching value to check
065      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(VALUE).thenPut(put2);
066      assertTrue(ok);
067
068      Put put3 = new Put(anotherrow);
069      put3.addColumn(FAMILY, QUALIFIER, VALUE);
070
071      // try to do CheckAndPut on different rows
072      try {
073        table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(value2).thenPut(put3);
074        fail("trying to check and modify different rows should have failed.");
075      } catch (Exception ignored) {
076      }
077    }
078  }
079
080  @TestTemplate
081  public void testCheckAndMutateWithTimeRange() throws IOException {
082    TEST_UTIL.createTable(tableName, FAMILY);
083    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
084      final long ts = EnvironmentEdgeManager.currentTime() / 2;
085      Put put = new Put(ROW);
086      put.addColumn(FAMILY, QUALIFIER, ts, VALUE);
087
088      boolean ok =
089        table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put);
090      assertTrue(ok);
091
092      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
093        .timeRange(TimeRange.at(ts + 10000)).ifEquals(VALUE).thenPut(put);
094      assertFalse(ok);
095
096      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
097        .timeRange(TimeRange.from(ts + 10000)).ifEquals(VALUE).thenPut(put);
098      assertFalse(ok);
099
100      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
101        .timeRange(TimeRange.between(ts + 10000, ts + 20000)).ifEquals(VALUE).thenPut(put);
102      assertFalse(ok);
103
104      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).timeRange(TimeRange.until(ts))
105        .ifEquals(VALUE).thenPut(put);
106      assertFalse(ok);
107
108      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).timeRange(TimeRange.at(ts))
109        .ifEquals(VALUE).thenPut(put);
110      assertTrue(ok);
111
112      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).timeRange(TimeRange.from(ts))
113        .ifEquals(VALUE).thenPut(put);
114      assertTrue(ok);
115
116      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
117        .timeRange(TimeRange.between(ts, ts + 20000)).ifEquals(VALUE).thenPut(put);
118      assertTrue(ok);
119
120      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
121        .timeRange(TimeRange.until(ts + 10000)).ifEquals(VALUE).thenPut(put);
122      assertTrue(ok);
123
124      RowMutations rm = new RowMutations(ROW).add((Mutation) put);
125      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
126        .timeRange(TimeRange.at(ts + 10000)).ifEquals(VALUE).thenMutate(rm);
127      assertFalse(ok);
128
129      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).timeRange(TimeRange.at(ts))
130        .ifEquals(VALUE).thenMutate(rm);
131      assertTrue(ok);
132
133      Delete delete = new Delete(ROW).addColumn(FAMILY, QUALIFIER);
134
135      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
136        .timeRange(TimeRange.at(ts + 10000)).ifEquals(VALUE).thenDelete(delete);
137      assertFalse(ok);
138
139      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).timeRange(TimeRange.at(ts))
140        .ifEquals(VALUE).thenDelete(delete);
141      assertTrue(ok);
142    }
143  }
144
145  @TestTemplate
146  public void testCheckAndPutWithCompareOp() throws IOException {
147    final byte[] value1 = Bytes.toBytes("aaaa");
148    final byte[] value2 = Bytes.toBytes("bbbb");
149    final byte[] value3 = Bytes.toBytes("cccc");
150    final byte[] value4 = Bytes.toBytes("dddd");
151    TEST_UTIL.createTable(tableName, FAMILY);
152    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
153      Put put2 = new Put(ROW);
154      put2.addColumn(FAMILY, QUALIFIER, value2);
155
156      Put put3 = new Put(ROW);
157      put3.addColumn(FAMILY, QUALIFIER, value3);
158
159      // row doesn't exist, so using "ifNotExists" should be considered "match".
160      boolean ok =
161        table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put2);
162      assertTrue(ok);
163
164      // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL
165      // turns out "match"
166      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
167        .ifMatches(CompareOperator.GREATER, value1).thenPut(put2);
168      assertFalse(ok);
169      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
170        .ifMatches(CompareOperator.EQUAL, value1).thenPut(put2);
171      assertFalse(ok);
172      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
173        .ifMatches(CompareOperator.GREATER_OR_EQUAL, value1).thenPut(put2);
174      assertFalse(ok);
175      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
176        .ifMatches(CompareOperator.LESS, value1).thenPut(put2);
177      assertTrue(ok);
178      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
179        .ifMatches(CompareOperator.LESS_OR_EQUAL, value1).thenPut(put2);
180      assertTrue(ok);
181      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
182        .ifMatches(CompareOperator.NOT_EQUAL, value1).thenPut(put3);
183      assertTrue(ok);
184
185      // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL
186      // turns out "match"
187      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
188        .ifMatches(CompareOperator.LESS, value4).thenPut(put3);
189      assertFalse(ok);
190      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
191        .ifMatches(CompareOperator.LESS_OR_EQUAL, value4).thenPut(put3);
192      assertFalse(ok);
193      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
194        .ifMatches(CompareOperator.EQUAL, value4).thenPut(put3);
195      assertFalse(ok);
196      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
197        .ifMatches(CompareOperator.GREATER, value4).thenPut(put3);
198      assertTrue(ok);
199      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
200        .ifMatches(CompareOperator.GREATER_OR_EQUAL, value4).thenPut(put3);
201      assertTrue(ok);
202      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
203        .ifMatches(CompareOperator.NOT_EQUAL, value4).thenPut(put2);
204      assertTrue(ok);
205
206      // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL
207      // turns out "match"
208      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
209        .ifMatches(CompareOperator.GREATER, value2).thenPut(put2);
210      assertFalse(ok);
211      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
212        .ifMatches(CompareOperator.NOT_EQUAL, value2).thenPut(put2);
213      assertFalse(ok);
214      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
215        .ifMatches(CompareOperator.LESS, value2).thenPut(put2);
216      assertFalse(ok);
217      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
218        .ifMatches(CompareOperator.GREATER_OR_EQUAL, value2).thenPut(put2);
219      assertTrue(ok);
220      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
221        .ifMatches(CompareOperator.LESS_OR_EQUAL, value2).thenPut(put2);
222      assertTrue(ok);
223      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
224        .ifMatches(CompareOperator.EQUAL, value2).thenPut(put3);
225      assertTrue(ok);
226    }
227  }
228
229  @TestTemplate
230  public void testCheckAndDelete() throws IOException {
231    final byte[] value1 = Bytes.toBytes("aaaa");
232    TEST_UTIL.createTable(tableName, FAMILY);
233    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
234      Put put = new Put(ROW);
235      put.addColumn(FAMILY, QUALIFIER, value1);
236      table.put(put);
237
238      Delete delete = new Delete(ROW);
239      delete.addColumns(FAMILY, QUALIFIER);
240
241      boolean ok =
242        table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(value1).thenDelete(delete);
243      assertTrue(ok);
244    }
245  }
246
247  @TestTemplate
248  public void testCheckAndDeleteWithCompareOp() throws IOException {
249    final byte[] value1 = Bytes.toBytes("aaaa");
250    final byte[] value2 = Bytes.toBytes("bbbb");
251    final byte[] value3 = Bytes.toBytes("cccc");
252    final byte[] value4 = Bytes.toBytes("dddd");
253    TEST_UTIL.createTable(tableName, FAMILY);
254    try (Connection conn = getConnection(); Table table = conn.getTable(tableName)) {
255      Put put2 = new Put(ROW);
256      put2.addColumn(FAMILY, QUALIFIER, value2);
257      table.put(put2);
258
259      Put put3 = new Put(ROW);
260      put3.addColumn(FAMILY, QUALIFIER, value3);
261
262      Delete delete = new Delete(ROW);
263      delete.addColumns(FAMILY, QUALIFIER);
264
265      // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL
266      // turns out "match"
267      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
268        .ifMatches(CompareOperator.GREATER, value1).thenDelete(delete);
269      assertFalse(ok);
270      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
271        .ifMatches(CompareOperator.EQUAL, value1).thenDelete(delete);
272      assertFalse(ok);
273      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
274        .ifMatches(CompareOperator.GREATER_OR_EQUAL, value1).thenDelete(delete);
275      assertFalse(ok);
276      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
277        .ifMatches(CompareOperator.LESS, value1).thenDelete(delete);
278      assertTrue(ok);
279      table.put(put2);
280      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
281        .ifMatches(CompareOperator.LESS_OR_EQUAL, value1).thenDelete(delete);
282      assertTrue(ok);
283      table.put(put2);
284      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
285        .ifMatches(CompareOperator.NOT_EQUAL, value1).thenDelete(delete);
286      assertTrue(ok);
287
288      // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL
289      // turns out "match"
290      table.put(put3);
291      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
292        .ifMatches(CompareOperator.LESS, value4).thenDelete(delete);
293      assertFalse(ok);
294      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
295        .ifMatches(CompareOperator.LESS_OR_EQUAL, value4).thenDelete(delete);
296      assertFalse(ok);
297      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
298        .ifMatches(CompareOperator.EQUAL, value4).thenDelete(delete);
299      assertFalse(ok);
300      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
301        .ifMatches(CompareOperator.GREATER, value4).thenDelete(delete);
302      assertTrue(ok);
303      table.put(put3);
304      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
305        .ifMatches(CompareOperator.GREATER_OR_EQUAL, value4).thenDelete(delete);
306      assertTrue(ok);
307      table.put(put3);
308      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
309        .ifMatches(CompareOperator.NOT_EQUAL, value4).thenDelete(delete);
310      assertTrue(ok);
311
312      // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL
313      // turns out "match"
314      table.put(put2);
315      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
316        .ifMatches(CompareOperator.GREATER, value2).thenDelete(delete);
317      assertFalse(ok);
318      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
319        .ifMatches(CompareOperator.NOT_EQUAL, value2).thenDelete(delete);
320      assertFalse(ok);
321      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
322        .ifMatches(CompareOperator.LESS, value2).thenDelete(delete);
323      assertFalse(ok);
324      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
325        .ifMatches(CompareOperator.GREATER_OR_EQUAL, value2).thenDelete(delete);
326      assertTrue(ok);
327      table.put(put2);
328      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
329        .ifMatches(CompareOperator.LESS_OR_EQUAL, value2).thenDelete(delete);
330      assertTrue(ok);
331      table.put(put2);
332      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
333        .ifMatches(CompareOperator.EQUAL, value2).thenDelete(delete);
334      assertTrue(ok);
335    }
336  }
337}