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