/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase; import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.NavigableMap; import java.util.TreeMap; import org.apache.hadoop.hbase.KeyValue.Type; import org.apache.hadoop.hbase.testclassification.SmallTests; import org.apache.hadoop.hbase.util.Bytes; import org.junit.Assert; import org.junit.Test; import org.junit.experimental.categories.Category; @Category(SmallTests.class) public class TestCellUtil { /** * CellScannable used in test. Returns a {@link TestCellScanner} */ private class TestCellScannable implements CellScannable { private final int cellsCount; TestCellScannable(final int cellsCount) { this.cellsCount = cellsCount; } @Override public CellScanner cellScanner() { return new TestCellScanner(this.cellsCount); } }; /** * CellScanner used in test. */ private class TestCellScanner implements CellScanner { private int count = 0; private Cell current = null; private final int cellsCount; TestCellScanner(final int cellsCount) { this.cellsCount = cellsCount; } @Override public Cell current() { return this.current; } @Override public boolean advance() throws IOException { if (this.count < cellsCount) { this.current = new TestCell(this.count); this.count++; return true; } return false; } } /** * Cell used in test. Has row only. */ private class TestCell implements Cell { private final byte [] row; TestCell(final int i) { this.row = Bytes.toBytes(i); } @Override public byte[] getRowArray() { return this.row; } @Override public int getRowOffset() { return 0; } @Override public short getRowLength() { return (short)this.row.length; } @Override public byte[] getFamilyArray() { // TODO Auto-generated method stub return null; } @Override public int getFamilyOffset() { // TODO Auto-generated method stub return 0; } @Override public byte getFamilyLength() { // TODO Auto-generated method stub return 0; } @Override public byte[] getQualifierArray() { // TODO Auto-generated method stub return null; } @Override public int getQualifierOffset() { // TODO Auto-generated method stub return 0; } @Override public int getQualifierLength() { // TODO Auto-generated method stub return 0; } @Override public long getTimestamp() { // TODO Auto-generated method stub return 0; } @Override public byte getTypeByte() { // TODO Auto-generated method stub return 0; } @Override public long getMvccVersion() { // TODO Auto-generated method stub return 0; } @Override public byte[] getValueArray() { // TODO Auto-generated method stub return null; } @Override public int getValueOffset() { // TODO Auto-generated method stub return 0; } @Override public int getValueLength() { // TODO Auto-generated method stub return 0; } @Override public byte[] getTagsArray() { // TODO Auto-generated method stub return null; } @Override public int getTagsOffset() { // TODO Auto-generated method stub return 0; } @Override public byte[] getValue() { // TODO Auto-generated method stub return null; } @Override public byte[] getFamily() { // TODO Auto-generated method stub return null; } @Override public byte[] getQualifier() { // TODO Auto-generated method stub return null; } @Override public byte[] getRow() { // TODO Auto-generated method stub return null; } @Override public long getSequenceId() { // TODO Auto-generated method stub return 0; } @Override public int getTagsLength() { // TODO Auto-generated method stub return 0; } }; /** * Was overflowing if 100k or so lists of cellscanners to return. * @throws IOException */ @Test public void testCreateCellScannerOverflow() throws IOException { consume(doCreateCellScanner(1, 1), 1 * 1); consume(doCreateCellScanner(3, 0), 3 * 0); consume(doCreateCellScanner(3, 3), 3 * 3); consume(doCreateCellScanner(0, 1), 0 * 1); // Do big number. See HBASE-11813 for why. final int hundredK = 100000; consume(doCreateCellScanner(hundredK, 0), hundredK * 0); consume(doCreateCellArray(1), 1); consume(doCreateCellArray(0), 0); consume(doCreateCellArray(3), 3); List<CellScannable> cells = new ArrayList<CellScannable>(hundredK); for (int i = 0; i < hundredK; i++) { cells.add(new TestCellScannable(1)); } consume(CellUtil.createCellScanner(cells), hundredK * 1); NavigableMap<byte [], List<Cell>> m = new TreeMap<byte [], List<Cell>>(Bytes.BYTES_COMPARATOR); List<Cell> cellArray = new ArrayList<Cell>(hundredK); for (int i = 0; i < hundredK; i++) cellArray.add(new TestCell(i)); m.put(new byte [] {'f'}, cellArray); consume(CellUtil.createCellScanner(m), hundredK * 1); } private CellScanner doCreateCellArray(final int itemsPerList) { Cell [] cells = new Cell [itemsPerList]; for (int i = 0; i < itemsPerList; i++) { cells[i] = new TestCell(i); } return CellUtil.createCellScanner(cells); } private CellScanner doCreateCellScanner(final int listsCount, final int itemsPerList) throws IOException { List<CellScannable> cells = new ArrayList<CellScannable>(listsCount); for (int i = 0; i < listsCount; i++) { CellScannable cs = new CellScannable() { @Override public CellScanner cellScanner() { return new TestCellScanner(itemsPerList); } }; cells.add(cs); } return CellUtil.createCellScanner(cells); } private void consume(final CellScanner scanner, final int expected) throws IOException { int count = 0; while (scanner.advance()) count++; Assert.assertEquals(expected, count); } @Test public void testOverlappingKeys() { byte[] empty = HConstants.EMPTY_BYTE_ARRAY; byte[] a = Bytes.toBytes("a"); byte[] b = Bytes.toBytes("b"); byte[] c = Bytes.toBytes("c"); byte[] d = Bytes.toBytes("d"); // overlaps Assert.assertTrue(CellUtil.overlappingKeys(a, b, a, b)); Assert.assertTrue(CellUtil.overlappingKeys(a, c, a, b)); Assert.assertTrue(CellUtil.overlappingKeys(a, b, a, c)); Assert.assertTrue(CellUtil.overlappingKeys(b, c, a, c)); Assert.assertTrue(CellUtil.overlappingKeys(a, c, b, c)); Assert.assertTrue(CellUtil.overlappingKeys(a, d, b, c)); Assert.assertTrue(CellUtil.overlappingKeys(b, c, a, d)); Assert.assertTrue(CellUtil.overlappingKeys(empty, b, a, b)); Assert.assertTrue(CellUtil.overlappingKeys(empty, b, a, c)); Assert.assertTrue(CellUtil.overlappingKeys(a, b, empty, b)); Assert.assertTrue(CellUtil.overlappingKeys(a, b, empty, c)); Assert.assertTrue(CellUtil.overlappingKeys(a, empty, a, b)); Assert.assertTrue(CellUtil.overlappingKeys(a, empty, a, c)); Assert.assertTrue(CellUtil.overlappingKeys(a, b, empty, empty)); Assert.assertTrue(CellUtil.overlappingKeys(empty, empty, a, b)); // non overlaps Assert.assertFalse(CellUtil.overlappingKeys(a, b, c, d)); Assert.assertFalse(CellUtil.overlappingKeys(c, d, a, b)); Assert.assertFalse(CellUtil.overlappingKeys(b, c, c, d)); Assert.assertFalse(CellUtil.overlappingKeys(b, c, c, empty)); Assert.assertFalse(CellUtil.overlappingKeys(b, c, d, empty)); Assert.assertFalse(CellUtil.overlappingKeys(c, d, b, c)); Assert.assertFalse(CellUtil.overlappingKeys(c, empty, b, c)); Assert.assertFalse(CellUtil.overlappingKeys(d, empty, b, c)); Assert.assertFalse(CellUtil.overlappingKeys(b, c, a, b)); Assert.assertFalse(CellUtil.overlappingKeys(b, c, empty, b)); Assert.assertFalse(CellUtil.overlappingKeys(b, c, empty, a)); Assert.assertFalse(CellUtil.overlappingKeys(a,b, b, c)); Assert.assertFalse(CellUtil.overlappingKeys(empty, b, b, c)); Assert.assertFalse(CellUtil.overlappingKeys(empty, a, b, c)); } @Test public void testFindCommonPrefixInFlatKey() { // The whole key matching case KeyValue kv1 = new KeyValue("r1".getBytes(), "f1".getBytes(), "q1".getBytes(), null); Assert.assertEquals(kv1.getKeyLength(), CellUtil.findCommonPrefixInFlatKey(kv1, kv1, true, true)); Assert.assertEquals(kv1.getKeyLength(), CellUtil.findCommonPrefixInFlatKey(kv1, kv1, false, true)); Assert.assertEquals(kv1.getKeyLength() - KeyValue.TIMESTAMP_TYPE_SIZE, CellUtil.findCommonPrefixInFlatKey(kv1, kv1, true, false)); // The rk length itself mismatch KeyValue kv2 = new KeyValue("r12".getBytes(), "f1".getBytes(), "q1".getBytes(), null); Assert.assertEquals(1, CellUtil.findCommonPrefixInFlatKey(kv1, kv2, true, true)); // part of rk is same KeyValue kv3 = new KeyValue("r14".getBytes(), "f1".getBytes(), "q1".getBytes(), null); Assert.assertEquals(KeyValue.ROW_LENGTH_SIZE + "r1".getBytes().length, CellUtil.findCommonPrefixInFlatKey(kv2, kv3, true, true)); // entire rk is same but different cf name KeyValue kv4 = new KeyValue("r14".getBytes(), "f2".getBytes(), "q1".getBytes(), null); Assert.assertEquals(KeyValue.ROW_LENGTH_SIZE + kv3.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE + "f".getBytes().length, CellUtil.findCommonPrefixInFlatKey(kv3, kv4, false, true)); // rk and family are same and part of qualifier KeyValue kv5 = new KeyValue("r14".getBytes(), "f2".getBytes(), "q123".getBytes(), null); Assert.assertEquals(KeyValue.ROW_LENGTH_SIZE + kv3.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE + kv4.getFamilyLength() + kv4.getQualifierLength(), CellUtil.findCommonPrefixInFlatKey(kv4, kv5, true, true)); // rk, cf and q are same. ts differs KeyValue kv6 = new KeyValue("rk".getBytes(), 1234L); KeyValue kv7 = new KeyValue("rk".getBytes(), 1235L); // only last byte out of 8 ts bytes in ts part differs Assert.assertEquals(KeyValue.ROW_LENGTH_SIZE + kv6.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE + kv6.getFamilyLength() + kv6.getQualifierLength() + 7, CellUtil.findCommonPrefixInFlatKey(kv6, kv7, true, true)); // rk, cf, q and ts are same. Only type differs KeyValue kv8 = new KeyValue("rk".getBytes(), 1234L, Type.Delete); Assert.assertEquals(KeyValue.ROW_LENGTH_SIZE + kv6.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE + kv6.getFamilyLength() + kv6.getQualifierLength() + KeyValue.TIMESTAMP_SIZE, CellUtil.findCommonPrefixInFlatKey(kv6, kv8, true, true)); // With out TS_TYPE check Assert.assertEquals(KeyValue.ROW_LENGTH_SIZE + kv6.getRowLength() + KeyValue.FAMILY_LENGTH_SIZE + kv6.getFamilyLength() + kv6.getQualifierLength(), CellUtil.findCommonPrefixInFlatKey(kv6, kv8, true, false)); } /** * Assert CellUtil makes Cell toStrings same way we do KeyValue toStrings. */ @Test public void testToString() { byte [] row = Bytes.toBytes("row"); long ts = 123l; // Make a KeyValue and a Cell and see if same toString result. KeyValue kv = new KeyValue(row, HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, ts, KeyValue.Type.Minimum, HConstants.EMPTY_BYTE_ARRAY); Cell cell = CellUtil.createCell(row, HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, ts, KeyValue.Type.Minimum.getCode(), HConstants.EMPTY_BYTE_ARRAY); String cellToString = CellUtil.getCellKeyAsString(cell); assertEquals(kv.toString(), cellToString); // Do another w/ non-null family. byte [] f = new byte [] {'f'}; byte [] q = new byte [] {'q'}; kv = new KeyValue(row, f, q, ts, KeyValue.Type.Minimum, HConstants.EMPTY_BYTE_ARRAY); cell = CellUtil.createCell(row, f, q, ts, KeyValue.Type.Minimum.getCode(), HConstants.EMPTY_BYTE_ARRAY); cellToString = CellUtil.getCellKeyAsString(cell); assertEquals(kv.toString(), cellToString); } }