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.filter; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertNull; 022 023import java.io.IOException; 024import java.nio.ByteBuffer; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.List; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.Cell; 030import org.apache.hadoop.hbase.CellUtil; 031import org.apache.hadoop.hbase.CompareOperator; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.HBaseTestingUtil; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.client.Durability; 037import org.apache.hadoop.hbase.client.Put; 038import org.apache.hadoop.hbase.client.Result; 039import org.apache.hadoop.hbase.client.ResultScanner; 040import org.apache.hadoop.hbase.client.Scan; 041import org.apache.hadoop.hbase.client.Table; 042import org.apache.hadoop.hbase.filter.FilterList.Operator; 043import org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy; 044import org.apache.hadoop.hbase.testclassification.FilterTests; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 048import org.apache.hadoop.hbase.util.Pair; 049import org.junit.AfterClass; 050import org.junit.BeforeClass; 051import org.junit.ClassRule; 052import org.junit.Rule; 053import org.junit.Test; 054import org.junit.experimental.categories.Category; 055import org.junit.rules.TestName; 056import org.slf4j.Logger; 057import org.slf4j.LoggerFactory; 058 059import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 060 061@Category({ FilterTests.class, MediumTests.class }) 062public class TestFuzzyRowFilterEndToEnd { 063 064 @ClassRule 065 public static final HBaseClassTestRule CLASS_RULE = 066 HBaseClassTestRule.forClass(TestFuzzyRowFilterEndToEnd.class); 067 068 private static final Logger LOG = LoggerFactory.getLogger(TestFuzzyRowFilterEndToEnd.class); 069 070 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 071 072 private static final byte fuzzyValue = (byte) 63; 073 074 @Rule 075 public TestName name = new TestName(); 076 077 @BeforeClass 078 public static void setUpBeforeClass() throws Exception { 079 Configuration conf = TEST_UTIL.getConfiguration(); 080 conf.setInt("hbase.client.scanner.caching", 1000); 081 conf.set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY, 082 ConstantSizeRegionSplitPolicy.class.getName()); 083 // set no splits 084 conf.setLong(HConstants.HREGION_MAX_FILESIZE, (1024L) * 1024 * 1024 * 10); 085 086 TEST_UTIL.startMiniCluster(); 087 } 088 089 @AfterClass 090 public static void tearDownAfterClass() throws Exception { 091 TEST_UTIL.shutdownMiniCluster(); 092 } 093 094 // HBASE-15676 Test that fuzzy info of all fixed bits (0s) finds matching row. 095 @Test 096 public void testAllFixedBits() throws IOException { 097 String cf = "f"; 098 String cq = "q"; 099 100 Table ht = TEST_UTIL.createTable(TableName.valueOf(name.getMethodName()), Bytes.toBytes(cf), 101 Integer.MAX_VALUE); 102 // Load data 103 String[] rows = new String[] { "\\x9C\\x00\\x044\\x00\\x00\\x00\\x00", 104 "\\x9C\\x00\\x044\\x01\\x00\\x00\\x00", "\\x9C\\x00\\x044\\x00\\x01\\x00\\x00", 105 "\\x9B\\x00\\x044e\\x9B\\x02\\xBB", "\\x9C\\x00\\x044\\x00\\x00\\x01\\x00", 106 "\\x9C\\x00\\x044\\x00\\x01\\x00\\x01", "\\x9B\\x00\\x044e\\xBB\\xB2\\xBB", }; 107 108 for (int i = 0; i < rows.length; i++) { 109 Put p = new Put(Bytes.toBytesBinary(rows[i])); 110 p.addColumn(Bytes.toBytes(cf), Bytes.toBytes(cq), Bytes.toBytes("value")); 111 ht.put(p); 112 } 113 114 TEST_UTIL.flush(); 115 116 List<Pair<byte[], byte[]>> data = new ArrayList<>(); 117 byte[] fuzzyKey = Bytes.toBytesBinary("\\x9B\\x00\\x044e"); 118 byte[] mask = new byte[] { 0, 0, 0, 0, 0 }; 119 120 // copy the fuzzy key and mask to test HBASE-18617 121 byte[] copyFuzzyKey = Arrays.copyOf(fuzzyKey, fuzzyKey.length); 122 byte[] copyMask = Arrays.copyOf(mask, mask.length); 123 124 data.add(new Pair<>(fuzzyKey, mask)); 125 FuzzyRowFilter filter = new FuzzyRowFilter(data); 126 127 Scan scan = new Scan(); 128 scan.setFilter(filter); 129 130 ResultScanner scanner = ht.getScanner(scan); 131 int total = 0; 132 while (scanner.next() != null) { 133 total++; 134 } 135 assertEquals(2, total); 136 137 assertEquals(true, Arrays.equals(copyFuzzyKey, fuzzyKey)); 138 assertEquals(true, Arrays.equals(copyMask, mask)); 139 140 TEST_UTIL.deleteTable(TableName.valueOf(name.getMethodName())); 141 } 142 143 @Test 144 public void testHBASE14782() throws IOException { 145 String cf = "f"; 146 String cq = "q"; 147 148 Table ht = TEST_UTIL.createTable(TableName.valueOf(name.getMethodName()), Bytes.toBytes(cf), 149 Integer.MAX_VALUE); 150 // Load data 151 String[] rows = 152 new String[] { "\\x9C\\x00\\x044\\x00\\x00\\x00\\x00", "\\x9C\\x00\\x044\\x01\\x00\\x00\\x00", 153 "\\x9C\\x00\\x044\\x00\\x01\\x00\\x00", "\\x9C\\x00\\x044\\x00\\x00\\x01\\x00", 154 "\\x9C\\x00\\x044\\x00\\x01\\x00\\x01", "\\x9B\\x00\\x044e\\xBB\\xB2\\xBB", }; 155 156 String badRow = "\\x9C\\x00\\x03\\xE9e\\xBB{X\\x1Fwts\\x1F\\x15vRX"; 157 158 for (int i = 0; i < rows.length; i++) { 159 Put p = new Put(Bytes.toBytesBinary(rows[i])); 160 p.addColumn(Bytes.toBytes(cf), Bytes.toBytes(cq), Bytes.toBytes("value")); 161 ht.put(p); 162 } 163 164 Put p = new Put(Bytes.toBytesBinary(badRow)); 165 p.addColumn(Bytes.toBytes(cf), Bytes.toBytes(cq), Bytes.toBytes("value")); 166 ht.put(p); 167 168 TEST_UTIL.flush(); 169 170 List<Pair<byte[], byte[]>> data = new ArrayList<>(); 171 byte[] fuzzyKey = Bytes.toBytesBinary("\\x00\\x00\\x044"); 172 byte[] mask = new byte[] { 1, 0, 0, 0 }; 173 data.add(new Pair<>(fuzzyKey, mask)); 174 FuzzyRowFilter filter = new FuzzyRowFilter(data); 175 176 Scan scan = new Scan(); 177 scan.setFilter(filter); 178 179 ResultScanner scanner = ht.getScanner(scan); 180 int total = 0; 181 while (scanner.next() != null) { 182 total++; 183 } 184 assertEquals(rows.length, total); 185 TEST_UTIL.deleteTable(TableName.valueOf(name.getMethodName())); 186 } 187 188 @Test 189 public void testFilterList() throws Exception { 190 String cf = "f"; 191 Table ht = TEST_UTIL.createTable(TableName.valueOf(name.getMethodName()), Bytes.toBytes(cf), 192 Integer.MAX_VALUE); 193 194 // 10 byte row key - (2 bytes 4 bytes 4 bytes) 195 // 4 byte qualifier 196 // 4 byte value 197 198 for (int i1 = 0; i1 < 5; i1++) { 199 for (int i2 = 0; i2 < 5; i2++) { 200 byte[] rk = new byte[10]; 201 202 ByteBuffer buf = ByteBuffer.wrap(rk); 203 buf.clear(); 204 buf.putShort((short) 2); 205 buf.putInt(i1); 206 buf.putInt(i2); 207 208 // Each row contains 5 columns 209 for (int c = 0; c < 5; c++) { 210 byte[] cq = new byte[4]; 211 Bytes.putBytes(cq, 0, Bytes.toBytes(c), 0, 4); 212 213 Put p = new Put(rk); 214 p.setDurability(Durability.SKIP_WAL); 215 p.addColumn(Bytes.toBytes(cf), cq, Bytes.toBytes(c)); 216 ht.put(p); 217 LOG.info( 218 "Inserting: rk: " + Bytes.toStringBinary(rk) + " cq: " + Bytes.toStringBinary(cq)); 219 } 220 } 221 } 222 223 TEST_UTIL.flush(); 224 225 // test passes if we get back 5 KV's (1 row) 226 runTest(ht, 5); 227 228 } 229 230 private void runTest(Table hTable, int expectedSize) throws IOException { 231 // [0, 2, ?, ?, ?, ?, 0, 0, 0, 1] 232 byte[] fuzzyKey1 = new byte[10]; 233 ByteBuffer buf = ByteBuffer.wrap(fuzzyKey1); 234 buf.clear(); 235 buf.putShort((short) 2); 236 for (int i = 0; i < 4; i++) 237 buf.put(fuzzyValue); 238 buf.putInt((short) 1); 239 byte[] mask1 = new byte[] { 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 }; 240 241 byte[] fuzzyKey2 = new byte[10]; 242 buf = ByteBuffer.wrap(fuzzyKey2); 243 buf.clear(); 244 buf.putShort((short) 2); 245 buf.putInt((short) 2); 246 for (int i = 0; i < 4; i++) 247 buf.put(fuzzyValue); 248 249 byte[] mask2 = new byte[] { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }; 250 251 Pair<byte[], byte[]> pair1 = new Pair<>(fuzzyKey1, mask1); 252 Pair<byte[], byte[]> pair2 = new Pair<>(fuzzyKey2, mask2); 253 254 FuzzyRowFilter fuzzyRowFilter1 = new FuzzyRowFilter(Lists.newArrayList(pair1)); 255 FuzzyRowFilter fuzzyRowFilter2 = new FuzzyRowFilter(Lists.newArrayList(pair2)); 256 // regular test - we expect 1 row back (5 KVs) 257 runScanner(hTable, expectedSize, fuzzyRowFilter1, fuzzyRowFilter2); 258 } 259 260 private void runScanner(Table hTable, int expectedSize, Filter filter1, Filter filter2) 261 throws IOException { 262 String cf = "f"; 263 Scan scan = new Scan(); 264 scan.addFamily(Bytes.toBytes(cf)); 265 FilterList filterList = new FilterList(Operator.MUST_PASS_ALL, filter1, filter2); 266 scan.setFilter(filterList); 267 268 ResultScanner scanner = hTable.getScanner(scan); 269 List<Cell> results = new ArrayList<>(); 270 Result result; 271 long timeBeforeScan = EnvironmentEdgeManager.currentTime(); 272 while ((result = scanner.next()) != null) { 273 for (Cell kv : result.listCells()) { 274 LOG.info("Got rk: " + Bytes.toStringBinary(CellUtil.cloneRow(kv)) + " cq: " 275 + Bytes.toStringBinary(CellUtil.cloneQualifier(kv))); 276 results.add(kv); 277 } 278 } 279 long scanTime = EnvironmentEdgeManager.currentTime() - timeBeforeScan; 280 scanner.close(); 281 282 LOG.info("scan time = " + scanTime + "ms"); 283 LOG.info("found " + results.size() + " results"); 284 285 assertEquals(expectedSize, results.size()); 286 } 287 288 @Test 289 public void testHBASE26967() throws IOException { 290 byte[] row1 = Bytes.toBytes("1"); 291 byte[] row2 = Bytes.toBytes("2"); 292 String cf1 = "f1"; 293 String cf2 = "f2"; 294 String cq1 = "col1"; 295 String cq2 = "col2"; 296 297 Table ht = 298 TEST_UTIL.createTable(TableName.valueOf(name.getMethodName()), new String[] { cf1, cf2 }); 299 300 // Put data 301 List<Put> puts = Lists.newArrayList(); 302 puts.add(new Put(row1).addColumn(Bytes.toBytes(cf1), Bytes.toBytes(cq1), Bytes.toBytes("a1"))); 303 puts.add(new Put(row1).addColumn(Bytes.toBytes(cf2), Bytes.toBytes(cq2), Bytes.toBytes("a2"))); 304 puts.add(new Put(row2).addColumn(Bytes.toBytes(cf1), Bytes.toBytes(cq1), Bytes.toBytes("b1"))); 305 puts.add(new Put(row2).addColumn(Bytes.toBytes(cf2), Bytes.toBytes(cq2), Bytes.toBytes("b2"))); 306 ht.put(puts); 307 308 TEST_UTIL.flush(); 309 310 // FuzzyRowFilter 311 List<Pair<byte[], byte[]>> data = Lists.newArrayList(); 312 byte[] fuzzyKey = Bytes.toBytes("1"); 313 byte[] mask = new byte[] { 0 }; 314 data.add(new Pair<>(fuzzyKey, mask)); 315 FuzzyRowFilter fuzzyRowFilter = new FuzzyRowFilter(data); 316 317 // SingleColumnValueFilter 318 Filter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes(cf2), 319 Bytes.toBytes(cq2), CompareOperator.EQUAL, Bytes.toBytes("x")); 320 321 // FilterList 322 FilterList filterList = new FilterList(Operator.MUST_PASS_ONE); 323 filterList.addFilter(Lists.newArrayList(fuzzyRowFilter, singleColumnValueFilter)); 324 325 Scan scan = new Scan(); 326 scan.setFilter(filterList); 327 328 ResultScanner scanner = ht.getScanner(scan); 329 Result rs = scanner.next(); 330 assertEquals(0, Bytes.compareTo(row1, rs.getRow())); 331 332 // The two cells (1,f1,col1,a1) (1,f2,col2,a2) 333 assertEquals(2, rs.listCells().size()); 334 335 // Only one row who's rowKey=1 336 assertNull(scanner.next()); 337 338 TEST_UTIL.deleteTable(TableName.valueOf(name.getMethodName())); 339 } 340}