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;
019
020import static org.junit.jupiter.api.Assertions.assertArrayEquals;
021import static org.junit.jupiter.api.Assertions.assertEquals;
022import static org.junit.jupiter.api.Assertions.assertFalse;
023import static org.junit.jupiter.api.Assertions.assertNotEquals;
024import static org.junit.jupiter.api.Assertions.assertNotNull;
025import static org.junit.jupiter.api.Assertions.assertTrue;
026import static org.junit.jupiter.api.Assertions.fail;
027
028import java.io.ByteArrayInputStream;
029import java.io.ByteArrayOutputStream;
030import java.io.DataInputStream;
031import java.io.DataOutputStream;
032import java.util.Collections;
033import java.util.Iterator;
034import java.util.List;
035import java.util.Set;
036import java.util.TreeSet;
037import org.apache.hadoop.hbase.testclassification.MiscTests;
038import org.apache.hadoop.hbase.testclassification.SmallTests;
039import org.apache.hadoop.hbase.util.ByteBufferUtils;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
042import org.junit.jupiter.api.Test;
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045
046@org.junit.jupiter.api.Tag(MiscTests.TAG)
047@org.junit.jupiter.api.Tag(SmallTests.TAG)
048public class TestKeyValue {
049  private static final Logger LOG = LoggerFactory.getLogger(TestKeyValue.class);
050
051  @Test
052  public void testColumnCompare() {
053    final byte[] a = Bytes.toBytes("aaa");
054    byte[] family1 = Bytes.toBytes("abc");
055    byte[] qualifier1 = Bytes.toBytes("def");
056    byte[] family2 = Bytes.toBytes("abcd");
057    byte[] qualifier2 = Bytes.toBytes("ef");
058
059    KeyValue aaa = new KeyValue(a, family1, qualifier1, 0L, KeyValue.Type.Put, a);
060    assertFalse(CellUtil.matchingColumn(aaa, family2, qualifier2));
061    assertTrue(CellUtil.matchingColumn(aaa, family1, qualifier1));
062    aaa = new KeyValue(a, family2, qualifier2, 0L, KeyValue.Type.Put, a);
063    assertFalse(CellUtil.matchingColumn(aaa, family1, qualifier1));
064    assertTrue(CellUtil.matchingColumn(aaa, family2, qualifier2));
065    byte[] nullQualifier = new byte[0];
066    aaa = new KeyValue(a, family1, nullQualifier, 0L, KeyValue.Type.Put, a);
067    assertTrue(CellUtil.matchingColumn(aaa, family1, null));
068    assertFalse(CellUtil.matchingColumn(aaa, family2, qualifier2));
069  }
070
071  /**
072   * Test a corner case when the family qualifier is a prefix of the column qualifier.
073   */
074  @Test
075  public void testColumnCompare_prefix() {
076    final byte[] a = Bytes.toBytes("aaa");
077    byte[] family1 = Bytes.toBytes("abc");
078    byte[] qualifier1 = Bytes.toBytes("def");
079    byte[] family2 = Bytes.toBytes("ab");
080    byte[] qualifier2 = Bytes.toBytes("def");
081
082    KeyValue aaa = new KeyValue(a, family1, qualifier1, 0L, KeyValue.Type.Put, a);
083    assertFalse(CellUtil.matchingColumn(aaa, family2, qualifier2));
084  }
085
086  @Test
087  public void testBasics() {
088    LOG.info("LOWKEY: " + KeyValue.LOWESTKEY.toString());
089    String name = "testBasics";
090    check(Bytes.toBytes(name), Bytes.toBytes(name), Bytes.toBytes(name), 1, Bytes.toBytes(name));
091    // Test empty value and empty column -- both should work. (not empty fam)
092    check(Bytes.toBytes(name), Bytes.toBytes(name), null, 1, null);
093    check(HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes(name), null, 1, null);
094    // empty qual is equivalent to null qual
095    assertEquals(new KeyValue(Bytes.toBytes("rk"), Bytes.toBytes("fam"), null, 1, (byte[]) null),
096      new KeyValue(Bytes.toBytes("rk"), Bytes.toBytes("fam"), HConstants.EMPTY_BYTE_ARRAY, 1,
097        (byte[]) null));
098  }
099
100  private void check(final byte[] row, final byte[] family, byte[] qualifier, final long timestamp,
101    final byte[] value) {
102    KeyValue kv = new KeyValue(row, family, qualifier, timestamp, value);
103    assertTrue(
104      Bytes.compareTo(kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(), row, 0, row.length)
105          == 0);
106    assertTrue(CellUtil.matchingColumn(kv, family, qualifier));
107    // Call toString to make sure it works.
108    LOG.info(kv.toString());
109  }
110
111  @Test
112  public void testPlainCompare() {
113    final byte[] a = Bytes.toBytes("aaa");
114    final byte[] b = Bytes.toBytes("bbb");
115    final byte[] fam = Bytes.toBytes("col");
116    final byte[] qf = Bytes.toBytes("umn");
117    KeyValue aaa = new KeyValue(a, fam, qf, a);
118    KeyValue bbb = new KeyValue(b, fam, qf, b);
119    assertTrue(CellComparatorImpl.COMPARATOR.compare(aaa, bbb) < 0);
120    assertTrue(CellComparatorImpl.COMPARATOR.compare(bbb, aaa) > 0);
121    // Compare breaks if passed same ByteBuffer as both left and right arguments.
122    assertTrue(CellComparatorImpl.COMPARATOR.compare(bbb, bbb) == 0);
123    assertTrue(CellComparatorImpl.COMPARATOR.compare(aaa, aaa) == 0);
124    // Do compare with different timestamps.
125    aaa = new KeyValue(a, fam, qf, 1, a);
126    bbb = new KeyValue(a, fam, qf, 2, a);
127    assertTrue(CellComparatorImpl.COMPARATOR.compare(aaa, bbb) > 0);
128    assertTrue(CellComparatorImpl.COMPARATOR.compare(bbb, aaa) < 0);
129    assertTrue(CellComparatorImpl.COMPARATOR.compare(aaa, aaa) == 0);
130    // Do compare with different types. Higher numbered types -- Delete
131    // should sort ahead of lower numbers; i.e. Put
132    aaa = new KeyValue(a, fam, qf, 1, KeyValue.Type.Delete, a);
133    bbb = new KeyValue(a, fam, qf, 1, a);
134    assertTrue(CellComparatorImpl.COMPARATOR.compare(aaa, bbb) < 0);
135    assertTrue(CellComparatorImpl.COMPARATOR.compare(bbb, aaa) > 0);
136    assertTrue(CellComparatorImpl.COMPARATOR.compare(aaa, aaa) == 0);
137  }
138
139  @Test
140  public void testMoreComparisons() {
141    long now = EnvironmentEdgeManager.currentTime();
142
143    // Meta compares
144    KeyValue aaa =
145      new KeyValue(Bytes.toBytes("TestScanMultipleVersions,row_0500,1236020145502"), now);
146    KeyValue bbb = new KeyValue(Bytes.toBytes("TestScanMultipleVersions,,99999999999999"), now);
147    CellComparator c = MetaCellComparator.META_COMPARATOR;
148    assertTrue(c.compare(bbb, aaa) < 0);
149
150    KeyValue aaaa = new KeyValue(Bytes.toBytes("TestScanMultipleVersions,,1236023996656"),
151      Bytes.toBytes("info"), Bytes.toBytes("regioninfo"), 1236024396271L, (byte[]) null);
152    assertTrue(c.compare(aaaa, bbb) < 0);
153
154    KeyValue x = new KeyValue(Bytes.toBytes("TestScanMultipleVersions,row_0500,1236034574162"),
155      Bytes.toBytes("info"), Bytes.toBytes(""), 9223372036854775807L, (byte[]) null);
156    KeyValue y = new KeyValue(Bytes.toBytes("TestScanMultipleVersions,row_0500,1236034574162"),
157      Bytes.toBytes("info"), Bytes.toBytes("regioninfo"), 1236034574912L, (byte[]) null);
158    assertTrue(c.compare(x, y) < 0);
159    comparisons(MetaCellComparator.META_COMPARATOR);
160    comparisons(CellComparatorImpl.COMPARATOR);
161    metacomparisons(MetaCellComparator.META_COMPARATOR);
162  }
163
164  @Test
165  public void testMetaComparatorTableKeysWithCommaOk() {
166    CellComparator c = MetaCellComparator.META_COMPARATOR;
167    long now = EnvironmentEdgeManager.currentTime();
168    // meta keys values are not quite right. A users can enter illegal values
169    // from shell when scanning meta.
170    KeyValue a = new KeyValue(Bytes.toBytes("table,key,with,commas1,1234"), now);
171    KeyValue b = new KeyValue(Bytes.toBytes("table,key,with,commas2,0123"), now);
172    assertTrue(c.compare(a, b) < 0);
173  }
174
175  /**
176   * Tests cases where rows keys have characters below the ','. See HBASE-832
177   */
178  @Test
179  public void testKeyValueBorderCases() {
180    // % sorts before , so if we don't do special comparator, rowB would
181    // come before rowA.
182    KeyValue rowA = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/,1234"),
183      Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[]) null);
184    KeyValue rowB = new KeyValue(Bytes.toBytes("testtable,www.hbase.org/%20,99999"),
185      Bytes.toBytes("fam"), Bytes.toBytes(""), Long.MAX_VALUE, (byte[]) null);
186    assertTrue(MetaCellComparator.META_COMPARATOR.compare(rowA, rowB) < 0);
187
188    rowA = new KeyValue(Bytes.toBytes("testtable,,1234"), Bytes.toBytes("fam"), Bytes.toBytes(""),
189      Long.MAX_VALUE, (byte[]) null);
190    rowB = new KeyValue(Bytes.toBytes("testtable,$www.hbase.org/,99999"), Bytes.toBytes("fam"),
191      Bytes.toBytes(""), Long.MAX_VALUE, (byte[]) null);
192    assertTrue(MetaCellComparator.META_COMPARATOR.compare(rowA, rowB) < 0);
193  }
194
195  private void metacomparisons(final CellComparatorImpl c) {
196    long now = EnvironmentEdgeManager.currentTime();
197    assertTrue(c.compare(
198      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",a,,0,1"), now),
199      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",a,,0,1"), now))
200        == 0);
201    KeyValue a =
202      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",a,,0,1"), now);
203    KeyValue b =
204      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",a,,0,2"), now);
205    assertTrue(c.compare(a, b) < 0);
206    assertTrue(c.compare(
207      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",a,,0,2"), now),
208      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",a,,0,1"), now))
209        > 0);
210  }
211
212  private void comparisons(final CellComparatorImpl c) {
213    long now = EnvironmentEdgeManager.currentTime();
214    assertTrue(c.compare(
215      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",,1"), now),
216      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",,1"), now)) == 0);
217    assertTrue(c.compare(
218      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",,1"), now),
219      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",,2"), now)) < 0);
220    assertTrue(c.compare(
221      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",,2"), now),
222      new KeyValue(Bytes.toBytes(TableName.META_TABLE_NAME.getNameAsString() + ",,1"), now)) > 0);
223  }
224
225  @Test
226  public void testBinaryKeys() {
227    Set<KeyValue> set = new TreeSet<>(CellComparatorImpl.COMPARATOR);
228    final byte[] fam = Bytes.toBytes("col");
229    final byte[] qf = Bytes.toBytes("umn");
230    final byte[] nb = new byte[0];
231    KeyValue[] keys = { new KeyValue(Bytes.toBytes("aaaaa,\u0000\u0000,2"), fam, qf, 2, nb),
232      new KeyValue(Bytes.toBytes("aaaaa,\u0001,3"), fam, qf, 3, nb),
233      new KeyValue(Bytes.toBytes("aaaaa,,1"), fam, qf, 1, nb),
234      new KeyValue(Bytes.toBytes("aaaaa,\u1000,5"), fam, qf, 5, nb),
235      new KeyValue(Bytes.toBytes("aaaaa,a,4"), fam, qf, 4, nb),
236      new KeyValue(Bytes.toBytes("a,a,0"), fam, qf, 0, nb), };
237    // Add to set with bad comparator
238    Collections.addAll(set, keys);
239    // This will output the keys incorrectly.
240    boolean assertion = false;
241    int count = 0;
242    for (KeyValue k : set) {
243      if (count++ != k.getTimestamp()) {
244        assertion = true;
245      }
246    }
247    assertTrue(assertion);
248    // Make set with good comparator
249    set = new TreeSet<>(MetaCellComparator.META_COMPARATOR);
250    Collections.addAll(set, keys);
251    count = 0;
252    for (KeyValue k : set) {
253      assertEquals(count++, k.getTimestamp());
254    }
255  }
256
257  @Test
258  public void testStackedUpKeyValue() {
259    // Test multiple KeyValues in a single blob.
260
261    // TODO actually write this test!
262  }
263
264  private final byte[] rowA = Bytes.toBytes("rowA");
265  private final byte[] rowB = Bytes.toBytes("rowB");
266
267  private final byte[] family = Bytes.toBytes("family");
268  private final byte[] qualA = Bytes.toBytes("qfA");
269
270  private void assertKVLess(CellComparator c, KeyValue less, KeyValue greater) {
271    int cmp = c.compare(less, greater);
272    assertTrue(cmp < 0);
273    cmp = c.compare(greater, less);
274    assertTrue(cmp > 0);
275  }
276
277  private void assertKVLessWithoutRow(CellComparator c, KeyValue less, KeyValue greater) {
278    int cmp = c.compare(less, greater);
279    assertTrue(cmp < 0);
280    cmp = c.compare(greater, less);
281    assertTrue(cmp > 0);
282  }
283
284  @Test
285  public void testCompareWithoutRow() {
286    final CellComparator c = CellComparatorImpl.COMPARATOR;
287    byte[] row = Bytes.toBytes("row");
288
289    byte[] fa = Bytes.toBytes("fa");
290    byte[] fami = Bytes.toBytes("fami");
291    byte[] fami1 = Bytes.toBytes("fami1");
292
293    byte[] qual0 = Bytes.toBytes("");
294    byte[] qual1 = Bytes.toBytes("qf1");
295    byte[] qual2 = Bytes.toBytes("qf2");
296    long ts = 1;
297
298    // 'fa:'
299    KeyValue kv_0 = new KeyValue(row, fa, qual0, ts, KeyValue.Type.Put);
300    // 'fami:'
301    KeyValue kv0_0 = new KeyValue(row, fami, qual0, ts, KeyValue.Type.Put);
302    // 'fami:qf1'
303    KeyValue kv0_1 = new KeyValue(row, fami, qual1, ts, KeyValue.Type.Put);
304    // 'fami:qf2'
305    KeyValue kv0_2 = new KeyValue(row, fami, qual2, ts, KeyValue.Type.Put);
306    // 'fami1:'
307    KeyValue kv1_0 = new KeyValue(row, fami1, qual0, ts, KeyValue.Type.Put);
308
309    // 'fami:qf1' < 'fami:qf2'
310    assertKVLessWithoutRow(c, kv0_1, kv0_2);
311    // 'fami:qf1' < 'fami1:'
312    assertKVLessWithoutRow(c, kv0_1, kv1_0);
313
314    // Test comparison by skipping the same prefix bytes.
315    /*
316     * KeyValue Format and commonLength:
317     * |_keyLen_|_valLen_|_rowLen_|_rowKey_|_famiLen_|_fami_|_Quali_|....
318     * ------------------|-------commonLength--------|--------------
319     */
320    // 'fa:' < 'fami:'. They have commonPrefix + 2 same prefix bytes.
321    assertKVLessWithoutRow(c, kv_0, kv0_0);
322    // 'fami:' < 'fami:qf1'. They have commonPrefix + 4 same prefix bytes.
323    assertKVLessWithoutRow(c, kv0_0, kv0_1);
324    // 'fami:qf1' < 'fami1:'. They have commonPrefix + 4 same prefix bytes.
325    assertKVLessWithoutRow(c, kv0_1, kv1_0);
326    // 'fami:qf1' < 'fami:qf2'. They have commonPrefix + 6 same prefix bytes.
327    assertKVLessWithoutRow(c, kv0_1, kv0_2);
328  }
329
330  @Test
331  public void testFirstLastOnRow() {
332    final CellComparator c = CellComparatorImpl.COMPARATOR;
333    long ts = 1;
334    byte[] bufferA = new byte[128];
335    int offsetA = 0;
336    byte[] bufferB = new byte[128];
337    int offsetB = 7;
338
339    // These are listed in sort order (ie: every one should be less
340    // than the one on the next line).
341    final KeyValue firstOnRowA = KeyValueUtil.createFirstOnRow(rowA);
342    final KeyValue firstOnRowABufferFamQual = KeyValueUtil.createFirstOnRow(bufferA, offsetA, rowA,
343      0, rowA.length, family, 0, family.length, qualA, 0, qualA.length);
344    final KeyValue kvA_1 = new KeyValue(rowA, null, null, ts, KeyValue.Type.Put);
345    final KeyValue kvA_2 = new KeyValue(rowA, family, qualA, ts, KeyValue.Type.Put);
346
347    final KeyValue lastOnRowA = KeyValueUtil.createLastOnRow(rowA);
348    final KeyValue firstOnRowB = KeyValueUtil.createFirstOnRow(rowB);
349    final KeyValue firstOnRowBBufferFam = KeyValueUtil.createFirstOnRow(bufferB, offsetB, rowB, 0,
350      rowB.length, family, 0, family.length, null, 0, 0);
351    final KeyValue kvB = new KeyValue(rowB, family, qualA, ts, KeyValue.Type.Put);
352
353    assertKVLess(c, firstOnRowA, firstOnRowB);
354    assertKVLess(c, firstOnRowA, firstOnRowBBufferFam);
355    assertKVLess(c, firstOnRowABufferFamQual, firstOnRowB);
356    assertKVLess(c, firstOnRowA, kvA_1);
357    assertKVLess(c, firstOnRowA, kvA_2);
358    assertKVLess(c, firstOnRowABufferFamQual, kvA_2);
359    assertKVLess(c, kvA_1, kvA_2);
360    assertKVLess(c, kvA_2, firstOnRowB);
361    assertKVLess(c, kvA_1, firstOnRowB);
362    assertKVLess(c, kvA_2, firstOnRowBBufferFam);
363    assertKVLess(c, kvA_1, firstOnRowBBufferFam);
364
365    assertKVLess(c, lastOnRowA, firstOnRowB);
366    assertKVLess(c, lastOnRowA, firstOnRowBBufferFam);
367    assertKVLess(c, firstOnRowB, kvB);
368    assertKVLess(c, firstOnRowBBufferFam, kvB);
369    assertKVLess(c, lastOnRowA, kvB);
370
371    assertKVLess(c, kvA_2, lastOnRowA);
372    assertKVLess(c, kvA_1, lastOnRowA);
373    assertKVLess(c, firstOnRowA, lastOnRowA);
374    assertKVLess(c, firstOnRowABufferFamQual, lastOnRowA);
375  }
376
377  @Test
378  public void testCreateKeyOnly() {
379    long ts = 1;
380    byte[] value = Bytes.toBytes("a real value");
381    byte[] evalue = new byte[0]; // empty value
382
383    for (byte[] val : new byte[][] { value, evalue }) {
384      for (boolean useLen : new boolean[] { false, true }) {
385        KeyValue kv1 = new KeyValue(rowA, family, qualA, ts, val);
386        KeyValue kv1ko = kv1.createKeyOnly(useLen);
387        // keys are still the same
388        assertTrue(kv1.equals(kv1ko));
389        // but values are not
390        assertTrue(kv1ko.getValueLength() == (useLen ? Bytes.SIZEOF_INT : 0));
391        if (useLen) {
392          assertEquals(kv1.getValueLength(),
393            Bytes.toInt(kv1ko.getValueArray(), kv1ko.getValueOffset(), kv1ko.getValueLength()));
394        }
395      }
396    }
397  }
398
399  @Test
400  public void testCreateKeyValueFromKey() {
401    KeyValue kv = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"),
402      Bytes.toBytes("myQualifier"), 12345L, Bytes.toBytes("myValue"));
403    int initialPadding = 10;
404    int endingPadding = 20;
405    int keyLen = kv.getKeyLength();
406    byte[] tmpArr = new byte[initialPadding + endingPadding + keyLen];
407    System.arraycopy(kv.getBuffer(), kv.getKeyOffset(), tmpArr, initialPadding, keyLen);
408    KeyValue kvFromKey = KeyValueUtil.createKeyValueFromKey(tmpArr, initialPadding, keyLen);
409    assertEquals(keyLen, kvFromKey.getKeyLength());
410    assertEquals(KeyValue.ROW_OFFSET + keyLen, kvFromKey.getBuffer().length);
411    System.err.println("kv=" + kv);
412    System.err.println("kvFromKey=" + kvFromKey);
413    assertEquals(kvFromKey.toString(), kv.toString().replaceAll("=[0-9]+", "=0"));
414  }
415
416  /**
417   * Tests that getTimestamp() does always return the proper timestamp, even after updating it. See
418   * HBASE-6265.
419   */
420  @Test
421  public void testGetTimestamp() {
422    KeyValue kv = new KeyValue(Bytes.toBytes("myRow"), Bytes.toBytes("myCF"),
423      Bytes.toBytes("myQualifier"), HConstants.LATEST_TIMESTAMP, Bytes.toBytes("myValue"));
424    long time1 = kv.getTimestamp();
425    kv.updateLatestStamp(Bytes.toBytes(12345L));
426    long time2 = kv.getTimestamp();
427    assertEquals(HConstants.LATEST_TIMESTAMP, time1);
428    assertEquals(12345L, time2);
429  }
430
431  @Test
432  public void testKVsWithTags() {
433    byte[] row = Bytes.toBytes("myRow");
434    byte[] cf = Bytes.toBytes("myCF");
435    byte[] q = Bytes.toBytes("myQualifier");
436    byte[] value = Bytes.toBytes("myValue");
437    byte[] metaValue1 = Bytes.toBytes("metaValue1");
438    byte[] metaValue2 = Bytes.toBytes("metaValue2");
439    KeyValue kv = new KeyValue(row, cf, q, HConstants.LATEST_TIMESTAMP, value, new Tag[] {
440      new ArrayBackedTag((byte) 1, metaValue1), new ArrayBackedTag((byte) 2, metaValue2) });
441    assertTrue(kv.getTagsLength() > 0);
442    assertTrue(
443      Bytes.equals(kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(), row, 0, row.length));
444    assertTrue(Bytes.equals(kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength(), cf, 0,
445      cf.length));
446    assertTrue(Bytes.equals(kv.getQualifierArray(), kv.getQualifierOffset(),
447      kv.getQualifierLength(), q, 0, q.length));
448    assertTrue(Bytes.equals(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength(), value, 0,
449      value.length));
450    List<Tag> tags = PrivateCellUtil.getTags(kv);
451    assertNotNull(tags);
452    assertEquals(2, tags.size());
453    boolean meta1Ok = false, meta2Ok = false;
454    for (Tag tag : tags) {
455      if (tag.getType() == (byte) 1) {
456        if (Bytes.equals(Tag.cloneValue(tag), metaValue1)) {
457          meta1Ok = true;
458        }
459      } else {
460        if (Bytes.equals(Tag.cloneValue(tag), metaValue2)) {
461          meta2Ok = true;
462        }
463      }
464    }
465    assertTrue(meta1Ok);
466    assertTrue(meta2Ok);
467    Iterator<Tag> tagItr = PrivateCellUtil.tagsIterator(kv);
468
469    assertTrue(tagItr.hasNext());
470    Tag next = tagItr.next();
471    assertEquals(10, next.getValueLength());
472    assertEquals((byte) 1, next.getType());
473    Bytes.equals(Tag.cloneValue(next), metaValue1);
474    assertTrue(tagItr.hasNext());
475    next = tagItr.next();
476    assertEquals(10, next.getValueLength());
477    assertEquals((byte) 2, next.getType());
478    Bytes.equals(Tag.cloneValue(next), metaValue2);
479    assertFalse(tagItr.hasNext());
480
481    tagItr = PrivateCellUtil.tagsIterator(kv);
482    assertTrue(tagItr.hasNext());
483    next = tagItr.next();
484    assertEquals(10, next.getValueLength());
485    assertEquals((byte) 1, next.getType());
486    Bytes.equals(Tag.cloneValue(next), metaValue1);
487    assertTrue(tagItr.hasNext());
488    next = tagItr.next();
489    assertEquals(10, next.getValueLength());
490    assertEquals((byte) 2, next.getType());
491    Bytes.equals(Tag.cloneValue(next), metaValue2);
492    assertFalse(tagItr.hasNext());
493  }
494
495  @Test
496  public void testMetaKeyComparator() {
497    CellComparator c = MetaCellComparator.META_COMPARATOR;
498    long now = EnvironmentEdgeManager.currentTime();
499
500    KeyValue a = new KeyValue(Bytes.toBytes("table1"), now);
501    KeyValue b = new KeyValue(Bytes.toBytes("table2"), now);
502    assertTrue(c.compare(a, b) < 0);
503
504    a = new KeyValue(Bytes.toBytes("table1,111"), now);
505    b = new KeyValue(Bytes.toBytes("table2"), now);
506    assertTrue(c.compare(a, b) < 0);
507
508    a = new KeyValue(Bytes.toBytes("table1"), now);
509    b = new KeyValue(Bytes.toBytes("table2,111"), now);
510    assertTrue(c.compare(a, b) < 0);
511
512    a = new KeyValue(Bytes.toBytes("table,111"), now);
513    b = new KeyValue(Bytes.toBytes("table,2222"), now);
514    assertTrue(c.compare(a, b) < 0);
515
516    a = new KeyValue(Bytes.toBytes("table,111,aaaa"), now);
517    b = new KeyValue(Bytes.toBytes("table,2222"), now);
518    assertTrue(c.compare(a, b) < 0);
519
520    a = new KeyValue(Bytes.toBytes("table,111"), now);
521    b = new KeyValue(Bytes.toBytes("table,2222.bbb"), now);
522    assertTrue(c.compare(a, b) < 0);
523
524    a = new KeyValue(Bytes.toBytes("table,,aaaa"), now);
525    b = new KeyValue(Bytes.toBytes("table,111,bbb"), now);
526    assertTrue(c.compare(a, b) < 0);
527
528    a = new KeyValue(Bytes.toBytes("table,111,aaaa"), now);
529    b = new KeyValue(Bytes.toBytes("table,111,bbb"), now);
530    assertTrue(c.compare(a, b) < 0);
531
532    a = new KeyValue(Bytes.toBytes("table,111,xxxx"), now);
533    b = new KeyValue(Bytes.toBytes("table,111,222,bbb"), now);
534    assertTrue(c.compare(a, b) < 0);
535
536    a = new KeyValue(Bytes.toBytes("table,111,11,xxx"), now);
537    b = new KeyValue(Bytes.toBytes("table,111,222,bbb"), now);
538    assertTrue(c.compare(a, b) < 0);
539  }
540
541  @Test
542  public void testEqualsAndHashCode() {
543    KeyValue kvA1 = new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes("qualA"),
544      Bytes.toBytes("1"));
545    KeyValue kvA2 = new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes("qualA"),
546      Bytes.toBytes("2"));
547    // We set a different sequence id on kvA2 to demonstrate that the equals and hashCode also
548    // don't take this into account.
549    kvA2.setSequenceId(2);
550    KeyValue kvB = new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes("qualB"),
551      Bytes.toBytes("1"));
552
553    assertEquals(kvA1, kvA2);
554    assertNotEquals(kvA1, kvB);
555    assertEquals(kvA1.hashCode(), kvA2.hashCode());
556    assertNotEquals(kvA1.hashCode(), kvB.hashCode());
557  }
558
559  @Test
560  public void testKeyValueSerialization() throws Exception {
561    KeyValue[] keyValues = new KeyValue[] {
562      new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes("qualA"),
563        Bytes.toBytes("1")),
564      new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes("qualA"),
565        Bytes.toBytes("2")),
566      new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes("qualA"),
567        EnvironmentEdgeManager.currentTime(), Bytes.toBytes("2"),
568        new Tag[] { new ArrayBackedTag((byte) 120, "tagA"),
569          new ArrayBackedTag((byte) 121, Bytes.toBytes("tagB")) }),
570      new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes("qualA"),
571        EnvironmentEdgeManager.currentTime(), Bytes.toBytes("2"),
572        new Tag[] { new ArrayBackedTag((byte) 0, "tagA") }),
573      new KeyValue(Bytes.toBytes("key"), Bytes.toBytes("cf"), Bytes.toBytes(""),
574        Bytes.toBytes("1")) };
575    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
576    for (KeyValue kv : keyValues) {
577      DataOutputStream os = new DataOutputStream(byteArrayOutputStream);
578      ByteBufferUtils.putInt(os, kv.getSerializedSize(true));
579      kv.write(os, true);
580    }
581    DataInputStream is =
582      new DataInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
583    for (int i = 0; i < keyValues.length; i++) {
584      LOG.info("Case#" + i + ": deserialize the kv: " + keyValues[i]);
585      KeyValue destKv = KeyValueUtil.createKeyValueFromInputStream(is, true);
586      assertEquals(keyValues[i], destKv);
587      assertArrayEquals(CellUtil.cloneValue(keyValues[i]), CellUtil.cloneValue(destKv));
588      assertArrayEquals(PrivateCellUtil.cloneTags(keyValues[i]), PrivateCellUtil.cloneTags(destKv));
589    }
590  }
591
592  @Test
593  public void testNullByteArrayKeyValueFailure() {
594    // can't add to testCheckKeyValueBytesFailureCase because it
595    // goes through the InputStream KeyValue API which can't produce a null buffer
596    try {
597      new KeyValue(null, 0, 0);
598    } catch (IllegalArgumentException iae) {
599      assertEquals("Invalid to have null byte array in KeyValue.", iae.getMessage());
600      return;
601    }
602    fail("Should have thrown an IllegalArgumentException when "
603      + "creating a KeyValue with a null buffer");
604  }
605
606  private static class FailureCase {
607    byte[] buf;
608    int offset;
609    int length;
610    boolean withTags;
611    String expectedMessage;
612
613    public FailureCase(byte[] buf, int offset, int length, boolean withTags,
614      String expectedMessage) {
615      this.buf = buf;
616      this.offset = offset;
617      this.length = length;
618      this.withTags = withTags;
619      this.expectedMessage = expectedMessage;
620    }
621
622    @Override
623    public String toString() {
624      return "FailureCaseDetails: [buf=" + Bytes.toStringBinary(buf, offset, length) + ", offset="
625        + offset + ", " + "length=" + length + ", expectedMessage=" + expectedMessage
626        + ", withtags=" + withTags + "]";
627    }
628
629    public String getExpectedMessage() {
630      return this.expectedMessage + KeyValueUtil.bytesToHex(buf, offset, length);
631    }
632  }
633
634  @Test
635  public void testCheckKeyValueBytesFailureCase() throws Exception {
636    byte[][] inputs = new byte[][] { HConstants.EMPTY_BYTE_ARRAY, // case.0
637      Bytes.toBytesBinary("a"), // case.1
638      Bytes.toBytesBinary("\\x00\\x00\\x00\\x01"), // case.2
639      Bytes.toBytesBinary("\\x00\\x00\\x00\\x01\\x00"), // case.3
640      Bytes.toBytesBinary("\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01"), // case.4
641      Bytes.toBytesBinary("\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00"), // case.5
642      Bytes.toBytesBinary("\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x01"), // case.6
643      Bytes.toBytesBinary("\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x03ROW"), // case.7
644      Bytes.toBytesBinary("\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01"), // case.8
645      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\xFF"
646        + "\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\xFF\\x03"), // case.9
647      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
648        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x03"), // case.10
649      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
650        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04"), // case.11
651      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
652        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04VALUE"), // case.12
653    };
654    String[] outputs = new String[] { "Overflow when reading key length at position=0",
655      "Overflow when reading key length at position=0",
656      "Invalid key length in KeyValue. keyLength=1",
657      "Overflow when reading value length at position=4",
658      "Invalid value length in KeyValue, valueLength=1",
659      "Overflow when reading row length at position=8",
660      "Invalid row length in KeyValue, rowLength=1",
661      "Overflow when reading family length at position=13",
662      "Invalid family length in KeyValue, familyLength=1", "Timestamp cannot be negative, ts=-1",
663      "Invalid type in KeyValue, type=3", "Overflow when reading value part at position=25",
664      "Invalid tags length in KeyValue at position=26" };
665    byte[][] withTagsInputs = new byte[][] {
666      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
667        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x01"), // case.13
668      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
669        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x00\\x01"), // case.14
670      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
671        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x00\\x04\\x00\\x03\\x00A"), // case.15
672      // case.16
673      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
674        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x00\\x0A\\x00\\x04\\x00TAG\\x00\\x04"
675        + "\\xFFT"),
676      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
677        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x00\\x0C\\x00\\x04\\x00TAG\\x00\\x05"
678        + "\\xF0COME\\x00"), // case.17
679      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
680        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x00\\x0C\\x00\\x04\\x00TAG\\x00\\x05"
681        + "\\xF0COME"), // case.18
682      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
683        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x00\\x00"), // case.19
684      Bytes.toBytesBinary("\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x01\\x00\\x03ROW\\x01FQ\\x00"
685        + "\\x00\\x00\\x00\\x00\\x00\\x00\\x01\\x04V\\x00\\x1B\\x00\\x05\\x01TAG1\\x00\\x05"
686        + "\\x02TAG2\\x00\\x05\\x03TAG3\\x00\\x05\\x04TAG4"), // case.20
687    };
688    String[] withTagsOutputs = new String[] { "Overflow when reading tags length at position=26",
689      "Invalid tags length in KeyValue at position=26",
690      "Invalid tag length at position=28, tagLength=3",
691      "Invalid tag length at position=34, tagLength=4",
692      "Some redundant bytes in KeyValue's buffer, startOffset=41, endOffset=42", null, null,
693      null, };
694    assertEquals(inputs.length, outputs.length);
695    assertEquals(withTagsInputs.length, withTagsOutputs.length);
696
697    FailureCase[] cases = new FailureCase[inputs.length + withTagsInputs.length];
698    for (int i = 0; i < inputs.length; i++) {
699      cases[i] = new FailureCase(inputs[i], 0, inputs[i].length, false, outputs[i]);
700    }
701    for (int i = 0; i < withTagsInputs.length; i++) {
702      cases[inputs.length + i] =
703        new FailureCase(withTagsInputs[i], 0, withTagsInputs[i].length, true, withTagsOutputs[i]);
704    }
705
706    for (int i = 0; i < cases.length; i++) {
707      FailureCase c = cases[i];
708      ByteArrayOutputStream baos = new ByteArrayOutputStream();
709      DataOutputStream os = new DataOutputStream(baos);
710      ByteBufferUtils.putInt(os, c.length);
711      os.write(c.buf, c.offset, c.length);
712      try {
713        KeyValueUtil.createKeyValueFromInputStream(
714          new DataInputStream(new ByteArrayInputStream(baos.toByteArray())), c.withTags);
715        if (c.expectedMessage != null) {
716          fail("Should fail when parse key value from an invalid bytes for case#" + i + ". " + c);
717        }
718      } catch (IllegalArgumentException e) {
719        assertEquals(c.getExpectedMessage(), e.getMessage(), "Case#" + i + " failed," + c);
720      }
721    }
722  }
723}