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.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.io.IOException;
026import java.math.BigDecimal;
027import java.nio.ByteBuffer;
028import java.util.ArrayList;
029import java.util.List;
030import java.util.NavigableMap;
031import java.util.TreeMap;
032import org.apache.hadoop.hbase.testclassification.MiscTests;
033import org.apache.hadoop.hbase.testclassification.SmallTests;
034import org.apache.hadoop.hbase.util.Bytes;
035import org.junit.Assert;
036import org.junit.ClassRule;
037import org.junit.Test;
038import org.junit.experimental.categories.Category;
039import org.mockito.Mockito;
040
041@Category({ MiscTests.class, SmallTests.class })
042public class TestCellUtil {
043  @ClassRule
044  public static final HBaseClassTestRule CLASS_RULE =
045    HBaseClassTestRule.forClass(TestCellUtil.class);
046
047  /**
048   * CellScannable used in test. Returns a {@link TestCellScanner}
049   */
050  private static class TestCellScannable implements CellScannable {
051    private final int cellsCount;
052
053    TestCellScannable(final int cellsCount) {
054      this.cellsCount = cellsCount;
055    }
056
057    @Override
058    public CellScanner cellScanner() {
059      return new TestCellScanner(this.cellsCount);
060    }
061  }
062
063  /**
064   * CellScanner used in test.
065   */
066  private static class TestCellScanner implements CellScanner {
067    private int count = 0;
068    private Cell current = null;
069    private final int cellsCount;
070
071    TestCellScanner(final int cellsCount) {
072      this.cellsCount = cellsCount;
073    }
074
075    @Override
076    public Cell current() {
077      return this.current;
078    }
079
080    @Override
081    public boolean advance() {
082      if (this.count < cellsCount) {
083        this.current = new TestCell(this.count);
084        this.count++;
085        return true;
086      }
087      return false;
088    }
089  }
090
091  /**
092   * Cell used in test. Has row only.
093   */
094  private static class TestCell implements ExtendedCell {
095    private final byte[] row;
096
097    TestCell(final int i) {
098      this.row = Bytes.toBytes(i);
099    }
100
101    @Override
102    public byte[] getRowArray() {
103      return this.row;
104    }
105
106    @Override
107    public int getRowOffset() {
108      return 0;
109    }
110
111    @Override
112    public short getRowLength() {
113      return (short) this.row.length;
114    }
115
116    @Override
117    public byte[] getFamilyArray() {
118      return null;
119    }
120
121    @Override
122    public int getFamilyOffset() {
123      return 0;
124    }
125
126    @Override
127    public byte getFamilyLength() {
128      return 0;
129    }
130
131    @Override
132    public byte[] getQualifierArray() {
133      return null;
134    }
135
136    @Override
137    public int getQualifierOffset() {
138      return 0;
139    }
140
141    @Override
142    public int getQualifierLength() {
143      return 0;
144    }
145
146    @Override
147    public long getTimestamp() {
148      return 0;
149    }
150
151    @Override
152    public byte getTypeByte() {
153      return 0;
154    }
155
156    @Override
157    public byte[] getValueArray() {
158      return null;
159    }
160
161    @Override
162    public int getValueOffset() {
163      return 0;
164    }
165
166    @Override
167    public int getValueLength() {
168      return 0;
169    }
170
171    @Override
172    public int getSerializedSize() {
173      return 0;
174    }
175
176    @Override
177    public byte[] getTagsArray() {
178      return null;
179    }
180
181    @Override
182    public int getTagsOffset() {
183      return 0;
184    }
185
186    @Override
187    public long getSequenceId() {
188      return 0;
189    }
190
191    @Override
192    public int getTagsLength() {
193      return 0;
194    }
195
196    @Override
197    public long heapSize() {
198      return 0;
199    }
200
201    @Override
202    public void setSequenceId(long seqId) throws IOException {
203
204    }
205
206    @Override
207    public void setTimestamp(long ts) throws IOException {
208
209    }
210
211    @Override
212    public void setTimestamp(byte[] ts) throws IOException {
213    }
214  }
215
216  /**
217   * Was overflowing if 100k or so lists of cellscanners to return.
218   */
219  @Test
220  public void testCreateCellScannerOverflow() throws IOException {
221    consume(doCreateCellScanner(1, 1), 1);
222    consume(doCreateCellScanner(3, 0), 0);
223    consume(doCreateCellScanner(3, 3), 3 * 3);
224    consume(doCreateCellScanner(0, 1), 0);
225    // Do big number. See HBASE-11813 for why.
226    final int hundredK = 100000;
227    consume(doCreateCellScanner(hundredK, 0), 0);
228    consume(doCreateCellArray(1), 1);
229    consume(doCreateCellArray(0), 0);
230    consume(doCreateCellArray(3), 3);
231    List<CellScannable> cells = new ArrayList<>(hundredK);
232    for (int i = 0; i < hundredK; i++) {
233      cells.add(new TestCellScannable(1));
234    }
235    consume(CellUtil.createCellScanner(cells), hundredK);
236    NavigableMap<byte[], List<Cell>> m = new TreeMap<>(Bytes.BYTES_COMPARATOR);
237    List<Cell> cellArray = new ArrayList<>(hundredK);
238    for (int i = 0; i < hundredK; i++) {
239      cellArray.add(new TestCell(i));
240    }
241    m.put(new byte[] { 'f' }, cellArray);
242    consume(CellUtil.createCellScanner(m), hundredK);
243  }
244
245  private CellScanner doCreateCellArray(final int itemsPerList) {
246    Cell[] cells = new Cell[itemsPerList];
247    for (int i = 0; i < itemsPerList; i++) {
248      cells[i] = new TestCell(i);
249    }
250    return CellUtil.createCellScanner(cells);
251  }
252
253  private CellScanner doCreateCellScanner(final int listsCount, final int itemsPerList) {
254    List<CellScannable> cells = new ArrayList<>(listsCount);
255    for (int i = 0; i < listsCount; i++) {
256      CellScannable cs = new CellScannable() {
257        @Override
258        public CellScanner cellScanner() {
259          return new TestCellScanner(itemsPerList);
260        }
261      };
262      cells.add(cs);
263    }
264    return CellUtil.createCellScanner(cells);
265  }
266
267  private void consume(final CellScanner scanner, final int expected) throws IOException {
268    int count = 0;
269    while (scanner.advance()) {
270      count++;
271    }
272    Assert.assertEquals(expected, count);
273  }
274
275  @Test
276  public void testOverlappingKeys() {
277    byte[] empty = HConstants.EMPTY_BYTE_ARRAY;
278    byte[] a = Bytes.toBytes("a");
279    byte[] b = Bytes.toBytes("b");
280    byte[] c = Bytes.toBytes("c");
281    byte[] d = Bytes.toBytes("d");
282
283    // overlaps
284    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, b, a, b));
285    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, c, a, b));
286    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, b, a, c));
287    Assert.assertTrue(PrivateCellUtil.overlappingKeys(b, c, a, c));
288    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, c, b, c));
289    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, d, b, c));
290    Assert.assertTrue(PrivateCellUtil.overlappingKeys(b, c, a, d));
291
292    Assert.assertTrue(PrivateCellUtil.overlappingKeys(empty, b, a, b));
293    Assert.assertTrue(PrivateCellUtil.overlappingKeys(empty, b, a, c));
294
295    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, b, empty, b));
296    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, b, empty, c));
297
298    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, empty, a, b));
299    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, empty, a, c));
300
301    Assert.assertTrue(PrivateCellUtil.overlappingKeys(a, b, empty, empty));
302    Assert.assertTrue(PrivateCellUtil.overlappingKeys(empty, empty, a, b));
303
304    // non overlaps
305    Assert.assertFalse(PrivateCellUtil.overlappingKeys(a, b, c, d));
306    Assert.assertFalse(PrivateCellUtil.overlappingKeys(c, d, a, b));
307
308    Assert.assertFalse(PrivateCellUtil.overlappingKeys(b, c, c, d));
309    Assert.assertFalse(PrivateCellUtil.overlappingKeys(b, c, c, empty));
310    Assert.assertFalse(PrivateCellUtil.overlappingKeys(b, c, d, empty));
311    Assert.assertFalse(PrivateCellUtil.overlappingKeys(c, d, b, c));
312    Assert.assertFalse(PrivateCellUtil.overlappingKeys(c, empty, b, c));
313    Assert.assertFalse(PrivateCellUtil.overlappingKeys(d, empty, b, c));
314
315    Assert.assertFalse(PrivateCellUtil.overlappingKeys(b, c, a, b));
316    Assert.assertFalse(PrivateCellUtil.overlappingKeys(b, c, empty, b));
317    Assert.assertFalse(PrivateCellUtil.overlappingKeys(b, c, empty, a));
318    Assert.assertFalse(PrivateCellUtil.overlappingKeys(a, b, b, c));
319    Assert.assertFalse(PrivateCellUtil.overlappingKeys(empty, b, b, c));
320    Assert.assertFalse(PrivateCellUtil.overlappingKeys(empty, a, b, c));
321  }
322
323  @Test
324  public void testFindCommonPrefixInFlatKey() {
325    // The whole key matching case
326    KeyValue kv1 =
327      new KeyValue(Bytes.toBytes("r1"), Bytes.toBytes("f1"), Bytes.toBytes("q1"), null);
328    Assert.assertEquals(kv1.getKeyLength(),
329      PrivateCellUtil.findCommonPrefixInFlatKey(kv1, kv1, true, true));
330    Assert.assertEquals(kv1.getKeyLength(),
331      PrivateCellUtil.findCommonPrefixInFlatKey(kv1, kv1, false, true));
332    Assert.assertEquals(kv1.getKeyLength() - KeyValue.TIMESTAMP_TYPE_SIZE,
333      PrivateCellUtil.findCommonPrefixInFlatKey(kv1, kv1, true, false));
334    // The rk length itself mismatch
335    KeyValue kv2 =
336      new KeyValue(Bytes.toBytes("r12"), Bytes.toBytes("f1"), Bytes.toBytes("q1"), null);
337    Assert.assertEquals(1, PrivateCellUtil.findCommonPrefixInFlatKey(kv1, kv2, true, true));
338    // part of rk is same
339    KeyValue kv3 =
340      new KeyValue(Bytes.toBytes("r14"), Bytes.toBytes("f1"), Bytes.toBytes("q1"), null);
341    Assert.assertEquals(KeyValue.ROW_LENGTH_SIZE + Bytes.toBytes("r1").length,
342      PrivateCellUtil.findCommonPrefixInFlatKey(kv2, kv3, true, true));
343    // entire rk is same but different cf name
344    KeyValue kv4 =
345      new KeyValue(Bytes.toBytes("r14"), Bytes.toBytes("f2"), Bytes.toBytes("q1"), null);
346    Assert.assertEquals(
347      KeyValue.ROW_LENGTH_SIZE + kv3.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE
348        + Bytes.toBytes("f").length,
349      PrivateCellUtil.findCommonPrefixInFlatKey(kv3, kv4, false, true));
350    // rk and family are same and part of qualifier
351    KeyValue kv5 =
352      new KeyValue(Bytes.toBytes("r14"), Bytes.toBytes("f2"), Bytes.toBytes("q123"), null);
353    Assert.assertEquals(
354      KeyValue.ROW_LENGTH_SIZE + kv3.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE
355        + kv4.getFamilyLength() + kv4.getQualifierLength(),
356      PrivateCellUtil.findCommonPrefixInFlatKey(kv4, kv5, true, true));
357    // rk, cf and q are same. ts differs
358    KeyValue kv6 = new KeyValue(Bytes.toBytes("rk"), 1234L);
359    KeyValue kv7 = new KeyValue(Bytes.toBytes("rk"), 1235L);
360    // only last byte out of 8 ts bytes in ts part differs
361    Assert.assertEquals(
362      KeyValue.ROW_LENGTH_SIZE + kv6.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE
363        + kv6.getFamilyLength() + kv6.getQualifierLength() + 7,
364      PrivateCellUtil.findCommonPrefixInFlatKey(kv6, kv7, true, true));
365    // rk, cf, q and ts are same. Only type differs
366    KeyValue kv8 = new KeyValue(Bytes.toBytes("rk"), 1234L, KeyValue.Type.Delete);
367    Assert.assertEquals(
368      KeyValue.ROW_LENGTH_SIZE + kv6.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE
369        + kv6.getFamilyLength() + kv6.getQualifierLength() + KeyValue.TIMESTAMP_SIZE,
370      PrivateCellUtil.findCommonPrefixInFlatKey(kv6, kv8, true, true));
371    // With out TS_TYPE check
372    Assert.assertEquals(
373      KeyValue.ROW_LENGTH_SIZE + kv6.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE
374        + kv6.getFamilyLength() + kv6.getQualifierLength(),
375      PrivateCellUtil.findCommonPrefixInFlatKey(kv6, kv8, true, false));
376  }
377
378  /**
379   * Assert CellUtil makes Cell toStrings same way we do KeyValue toStrings.
380   */
381  @Test
382  public void testToString() {
383    byte[] row = Bytes.toBytes("row");
384    long ts = 123L;
385    // Make a KeyValue and a Cell and see if same toString result.
386    KeyValue kv = new KeyValue(row, HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, ts,
387      KeyValue.Type.Minimum, HConstants.EMPTY_BYTE_ARRAY);
388    Cell cell = ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row)
389      .setFamily(HConstants.EMPTY_BYTE_ARRAY).setQualifier(HConstants.EMPTY_BYTE_ARRAY)
390      .setTimestamp(ts).setType(KeyValue.Type.Minimum.getCode())
391      .setValue(HConstants.EMPTY_BYTE_ARRAY).build();
392    String cellToString = CellUtil.getCellKeyAsString(cell);
393    assertEquals(kv.toString(), cellToString);
394    // Do another w/ non-null family.
395    byte[] f = new byte[] { 'f' };
396    byte[] q = new byte[] { 'q' };
397    kv = new KeyValue(row, f, q, ts, KeyValue.Type.Minimum, HConstants.EMPTY_BYTE_ARRAY);
398    cell = ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY).setRow(row).setFamily(f)
399      .setQualifier(q).setTimestamp(ts).setType(KeyValue.Type.Minimum.getCode())
400      .setValue(HConstants.EMPTY_BYTE_ARRAY).build();
401    cellToString = CellUtil.getCellKeyAsString(cell);
402    assertEquals(kv.toString(), cellToString);
403  }
404
405  @Test
406  public void testToString1() {
407    String row = "test.row";
408    String family = "test.family";
409    String qualifier = "test.qualifier";
410    long timestamp = 42;
411    KeyValue.Type type = KeyValue.Type.Put;
412    String value = "test.value";
413    long seqId = 1042;
414
415    Cell cell = ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY)
416      .setRow(Bytes.toBytes(row)).setFamily(Bytes.toBytes(family))
417      .setQualifier(Bytes.toBytes(qualifier)).setTimestamp(timestamp).setType(type.getCode())
418      .setValue(Bytes.toBytes(value)).setSequenceId(seqId).build();
419
420    String nonVerbose = CellUtil.toString(cell, false);
421    String verbose = CellUtil.toString(cell, true);
422
423    System.out.println("nonVerbose=" + nonVerbose);
424    System.out.println("verbose=" + verbose);
425
426    Assert.assertEquals(String.format("%s/%s:%s/%d/%s/vlen=%s/seqid=%s", row, family, qualifier,
427      timestamp, type.toString(), Bytes.toBytes(value).length, seqId), nonVerbose);
428
429    Assert.assertEquals(String.format("%s/%s:%s/%d/%s/vlen=%s/seqid=%s/%s", row, family, qualifier,
430      timestamp, type.toString(), Bytes.toBytes(value).length, seqId, value), verbose);
431
432    // TODO: test with tags
433  }
434
435  @Test
436  public void testCloneCellFieldsFromByteBufferedCell() {
437    byte[] r = Bytes.toBytes("row1");
438    byte[] f = Bytes.toBytes("cf1");
439    byte[] q = Bytes.toBytes("qual1");
440    byte[] v = Bytes.toBytes("val1");
441    byte[] tags = Bytes.toBytes("tag1");
442    KeyValue kv =
443      new KeyValue(r, f, q, 0, q.length, 1234L, KeyValue.Type.Put, v, 0, v.length, tags);
444    ByteBuffer buffer = ByteBuffer.wrap(kv.getBuffer());
445    ExtendedCell bbCell = new ByteBufferKeyValue(buffer, 0, buffer.remaining());
446    byte[] rDest = CellUtil.cloneRow(bbCell);
447    assertTrue(Bytes.equals(r, rDest));
448    byte[] fDest = CellUtil.cloneFamily(bbCell);
449    assertTrue(Bytes.equals(f, fDest));
450    byte[] qDest = CellUtil.cloneQualifier(bbCell);
451    assertTrue(Bytes.equals(q, qDest));
452    byte[] vDest = CellUtil.cloneValue(bbCell);
453    assertTrue(Bytes.equals(v, vDest));
454    byte[] tDest = new byte[tags.length];
455    PrivateCellUtil.copyTagsTo(bbCell, tDest, 0);
456    assertTrue(Bytes.equals(tags, tDest));
457  }
458
459  @Test
460  public void testMatchingCellFieldsFromByteBufferedCell() {
461    byte[] r = Bytes.toBytes("row1");
462    byte[] f = Bytes.toBytes("cf1");
463    byte[] q1 = Bytes.toBytes("qual1");
464    byte[] q2 = Bytes.toBytes("qual2");
465    byte[] v = Bytes.toBytes("val1");
466    byte[] tags = Bytes.toBytes("tag1");
467    KeyValue kv =
468      new KeyValue(r, f, q1, 0, q1.length, 1234L, KeyValue.Type.Put, v, 0, v.length, tags);
469    ByteBuffer buffer = ByteBuffer.wrap(kv.getBuffer());
470    Cell bbCell1 = new ByteBufferKeyValue(buffer, 0, buffer.remaining());
471    kv = new KeyValue(r, f, q2, 0, q2.length, 1234L, KeyValue.Type.Put, v, 0, v.length, tags);
472    buffer = ByteBuffer.wrap(kv.getBuffer());
473    Cell bbCell2 = new ByteBufferKeyValue(buffer, 0, buffer.remaining());
474    assertTrue(CellUtil.matchingRows(bbCell1, bbCell2));
475    assertTrue(CellUtil.matchingRows(kv, bbCell2));
476    assertTrue(CellUtil.matchingRows(bbCell1, r));
477    assertTrue(CellUtil.matchingFamily(bbCell1, bbCell2));
478    assertTrue(CellUtil.matchingFamily(kv, bbCell2));
479    assertTrue(CellUtil.matchingFamily(bbCell1, f));
480    assertFalse(CellUtil.matchingQualifier(bbCell1, bbCell2));
481    assertTrue(CellUtil.matchingQualifier(kv, bbCell2));
482    assertTrue(CellUtil.matchingQualifier(bbCell1, q1));
483    assertTrue(CellUtil.matchingQualifier(bbCell2, q2));
484    assertTrue(CellUtil.matchingValue(bbCell1, bbCell2));
485    assertTrue(CellUtil.matchingValue(kv, bbCell2));
486    assertTrue(CellUtil.matchingValue(bbCell1, v));
487    assertFalse(CellUtil.matchingColumn(bbCell1, bbCell2));
488    assertTrue(CellUtil.matchingColumn(kv, bbCell2));
489    assertTrue(CellUtil.matchingColumn(bbCell1, f, q1));
490    assertTrue(CellUtil.matchingColumn(bbCell2, f, q2));
491  }
492
493  @Test
494  public void testCellFieldsAsPrimitiveTypesFromByteBufferedCell() {
495    int ri = 123;
496    byte[] r = Bytes.toBytes(ri);
497    byte[] f = Bytes.toBytes("cf1");
498    byte[] q = Bytes.toBytes("qual1");
499    long vl = 10981L;
500    byte[] v = Bytes.toBytes(vl);
501    KeyValue kv = new KeyValue(r, f, q, v);
502    ByteBuffer buffer = ByteBuffer.wrap(kv.getBuffer());
503    Cell bbCell = new ByteBufferKeyValue(buffer, 0, buffer.remaining());
504    assertEquals(ri, PrivateCellUtil.getRowAsInt(bbCell));
505    assertEquals(vl, PrivateCellUtil.getValueAsLong(bbCell));
506    double vd = 3005.5;
507    v = Bytes.toBytes(vd);
508    kv = new KeyValue(r, f, q, v);
509    buffer = ByteBuffer.wrap(kv.getBuffer());
510    bbCell = new ByteBufferKeyValue(buffer, 0, buffer.remaining());
511    assertEquals(vd, PrivateCellUtil.getValueAsDouble(bbCell), 0.0);
512    BigDecimal bd = new BigDecimal(9999);
513    v = Bytes.toBytes(bd);
514    kv = new KeyValue(r, f, q, v);
515    buffer = ByteBuffer.wrap(kv.getBuffer());
516    bbCell = new ByteBufferKeyValue(buffer, 0, buffer.remaining());
517    assertEquals(bd, PrivateCellUtil.getValueAsBigDecimal(bbCell));
518  }
519
520  @Test
521  public void testGetType() {
522    ExtendedCell c = Mockito.mock(ExtendedCell.class);
523    Mockito.when(c.getType()).thenCallRealMethod();
524    for (Cell.Type type : Cell.Type.values()) {
525      Mockito.when(c.getTypeByte()).thenReturn(type.getCode());
526      assertEquals(type, c.getType());
527    }
528
529    try {
530      Mockito.when(c.getTypeByte()).thenReturn(KeyValue.Type.Maximum.getCode());
531      c.getType();
532      fail("The code of Maximum can't be handled by Cell.Type");
533    } catch (UnsupportedOperationException e) {
534    }
535
536    try {
537      Mockito.when(c.getTypeByte()).thenReturn(KeyValue.Type.Minimum.getCode());
538      c.getType();
539      fail("The code of Maximum can't be handled by Cell.Type");
540    } catch (UnsupportedOperationException e) {
541    }
542  }
543}