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