1   /*
2    * Copyright 2009 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.regionserver;
22  
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.hadoop.hbase.HBaseTestCase;
28  import org.apache.hadoop.hbase.KeyValue;
29  import org.apache.hadoop.hbase.KeyValue.KVComparator;
30  import org.apache.hadoop.hbase.SmallTests;
31  import org.apache.hadoop.hbase.client.Get;
32  import org.apache.hadoop.hbase.client.Scan;
33  import org.apache.hadoop.hbase.regionserver.ScanQueryMatcher.MatchCode;
34  import org.apache.hadoop.hbase.util.Bytes;
35  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
36  import org.junit.experimental.categories.Category;
37  
38  @Category(SmallTests.class)
39  public class TestQueryMatcher extends HBaseTestCase {
40    private static final boolean PRINT = false;
41  
42    private byte[] row1;
43    private byte[] row2;
44    private byte[] fam1;
45    private byte[] fam2;
46    private byte[] col1;
47    private byte[] col2;
48    private byte[] col3;
49    private byte[] col4;
50    private byte[] col5;
51  
52    private byte[] data;
53  
54    private Get get;
55  
56    long ttl = Long.MAX_VALUE;
57    KVComparator rowComparator;
58    private Scan scan;
59  
60    public void setUp() throws Exception {
61      super.setUp();
62      row1 = Bytes.toBytes("row1");
63      row2 = Bytes.toBytes("row2");
64      fam1 = Bytes.toBytes("fam1");
65      fam2 = Bytes.toBytes("fam2");
66      col1 = Bytes.toBytes("col1");
67      col2 = Bytes.toBytes("col2");
68      col3 = Bytes.toBytes("col3");
69      col4 = Bytes.toBytes("col4");
70      col5 = Bytes.toBytes("col5");
71  
72      data = Bytes.toBytes("data");
73  
74      //Create Get
75      get = new Get(row1);
76      get.addFamily(fam1);
77      get.addColumn(fam2, col2);
78      get.addColumn(fam2, col4);
79      get.addColumn(fam2, col5);
80      this.scan = new Scan(get);
81  
82      rowComparator = KeyValue.COMPARATOR;
83  
84    }
85  
86    private void _testMatch_ExplicitColumns(Scan scan, List<MatchCode> expected) throws IOException {
87      //Moving up from the Tracker by using Gets and List<KeyValue> instead
88      //of just byte []
89  
90      // 2,4,5    
91      ScanQueryMatcher qm = new ScanQueryMatcher(scan, new Store.ScanInfo(fam2,
92          0, 1, ttl, false, 0, rowComparator), get.getFamilyMap().get(fam2),
93          EnvironmentEdgeManager.currentTimeMillis() - ttl);
94  
95      List<KeyValue> memstore = new ArrayList<KeyValue>();
96      memstore.add(new KeyValue(row1, fam2, col1, 1, data));
97      memstore.add(new KeyValue(row1, fam2, col2, 1, data));
98      memstore.add(new KeyValue(row1, fam2, col3, 1, data));
99      memstore.add(new KeyValue(row1, fam2, col4, 1, data));
100     memstore.add(new KeyValue(row1, fam2, col5, 1, data));
101 
102     memstore.add(new KeyValue(row2, fam1, col1, data));
103 
104     List<ScanQueryMatcher.MatchCode> actual = new ArrayList<ScanQueryMatcher.MatchCode>();
105     KeyValue k = memstore.get(0);
106     qm.setRow(k.getBuffer(), k.getRowOffset(), k.getRowLength());
107 
108     for (KeyValue kv : memstore){
109       actual.add(qm.match(kv));
110     }
111 
112     assertEquals(expected.size(), actual.size());
113     for(int i=0; i< expected.size(); i++){
114       assertEquals(expected.get(i), actual.get(i));
115       if(PRINT){
116         System.out.println("expected "+expected.get(i)+
117             ", actual " +actual.get(i));
118       }
119     }
120   }
121 
122   public void testMatch_ExplicitColumns()
123   throws IOException {
124     //Moving up from the Tracker by using Gets and List<KeyValue> instead
125     //of just byte []
126 
127     //Expected result
128     List<MatchCode> expected = new ArrayList<ScanQueryMatcher.MatchCode>();
129     expected.add(ScanQueryMatcher.MatchCode.SEEK_NEXT_COL);
130     expected.add(ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL);
131     expected.add(ScanQueryMatcher.MatchCode.SEEK_NEXT_COL);
132     expected.add(ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL);
133     expected.add(ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_ROW);
134     expected.add(ScanQueryMatcher.MatchCode.DONE);
135 
136     _testMatch_ExplicitColumns(scan, expected);
137   }
138 
139   public void testMatch_ExplicitColumnsWithLookAhead()
140   throws IOException {
141     //Moving up from the Tracker by using Gets and List<KeyValue> instead
142     //of just byte []
143 
144     //Expected result
145     List<MatchCode> expected = new ArrayList<ScanQueryMatcher.MatchCode>();
146     expected.add(ScanQueryMatcher.MatchCode.SKIP);
147     expected.add(ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL);
148     expected.add(ScanQueryMatcher.MatchCode.SKIP);
149     expected.add(ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL);
150     expected.add(ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_ROW);
151     expected.add(ScanQueryMatcher.MatchCode.DONE);
152 
153     Scan s = new Scan(scan);
154     s.setAttribute(Scan.HINT_LOOKAHEAD, Bytes.toBytes(2));
155     _testMatch_ExplicitColumns(s, expected);
156   }
157 
158 
159   public void testMatch_Wildcard()
160   throws IOException {
161     //Moving up from the Tracker by using Gets and List<KeyValue> instead
162     //of just byte []
163 
164     //Expected result
165     List<MatchCode> expected = new ArrayList<ScanQueryMatcher.MatchCode>();
166     expected.add(ScanQueryMatcher.MatchCode.INCLUDE);
167     expected.add(ScanQueryMatcher.MatchCode.INCLUDE);
168     expected.add(ScanQueryMatcher.MatchCode.INCLUDE);
169     expected.add(ScanQueryMatcher.MatchCode.INCLUDE);
170     expected.add(ScanQueryMatcher.MatchCode.INCLUDE);
171     expected.add(ScanQueryMatcher.MatchCode.DONE);
172 
173     ScanQueryMatcher qm = new ScanQueryMatcher(scan, new Store.ScanInfo(fam2,
174         0, 1, ttl, false, 0, rowComparator), null,
175         EnvironmentEdgeManager.currentTimeMillis() - ttl);
176 
177     List<KeyValue> memstore = new ArrayList<KeyValue>();
178     memstore.add(new KeyValue(row1, fam2, col1, 1, data));
179     memstore.add(new KeyValue(row1, fam2, col2, 1, data));
180     memstore.add(new KeyValue(row1, fam2, col3, 1, data));
181     memstore.add(new KeyValue(row1, fam2, col4, 1, data));
182     memstore.add(new KeyValue(row1, fam2, col5, 1, data));
183     memstore.add(new KeyValue(row2, fam1, col1, 1, data));
184 
185     List<ScanQueryMatcher.MatchCode> actual = new ArrayList<ScanQueryMatcher.MatchCode>();
186 
187     KeyValue k = memstore.get(0);
188     qm.setRow(k.getBuffer(), k.getRowOffset(), k.getRowLength());
189 
190     for(KeyValue kv : memstore) {
191       actual.add(qm.match(kv));
192     }
193 
194     assertEquals(expected.size(), actual.size());
195     for(int i=0; i< expected.size(); i++){
196       assertEquals(expected.get(i), actual.get(i));
197       if(PRINT){
198         System.out.println("expected "+expected.get(i)+
199             ", actual " +actual.get(i));
200       }
201     }
202   }
203 
204 
205   /**
206    * Verify that {@link ScanQueryMatcher} only skips expired KeyValue
207    * instances and does not exit early from the row (skipping
208    * later non-expired KeyValues).  This version mimics a Get with
209    * explicitly specified column qualifiers.
210    *
211    * @throws IOException
212    */
213   public void testMatch_ExpiredExplicit()
214   throws IOException {
215 
216     long testTTL = 1000;
217     MatchCode [] expected = new MatchCode[] {
218         ScanQueryMatcher.MatchCode.SEEK_NEXT_COL,
219         ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL,
220         ScanQueryMatcher.MatchCode.SEEK_NEXT_COL,
221         ScanQueryMatcher.MatchCode.INCLUDE_AND_SEEK_NEXT_COL,
222         ScanQueryMatcher.MatchCode.SEEK_NEXT_ROW,
223         ScanQueryMatcher.MatchCode.DONE
224     };
225 
226     long now = EnvironmentEdgeManager.currentTimeMillis();
227     ScanQueryMatcher qm = new ScanQueryMatcher(scan, new Store.ScanInfo(fam2,
228         0, 1, testTTL, false, 0, rowComparator), get.getFamilyMap().get(fam2),
229         now - testTTL);
230 
231     KeyValue [] kvs = new KeyValue[] {
232         new KeyValue(row1, fam2, col1, now-100, data),
233         new KeyValue(row1, fam2, col2, now-50, data),
234         new KeyValue(row1, fam2, col3, now-5000, data),
235         new KeyValue(row1, fam2, col4, now-500, data),
236         new KeyValue(row1, fam2, col5, now-10000, data),
237         new KeyValue(row2, fam1, col1, now-10, data)
238     };
239 
240     KeyValue k = kvs[0];
241     qm.setRow(k.getBuffer(), k.getRowOffset(), k.getRowLength());
242 
243     List<MatchCode> actual = new ArrayList<MatchCode>(kvs.length);
244     for (KeyValue kv : kvs) {
245       actual.add( qm.match(kv) );
246     }
247 
248     assertEquals(expected.length, actual.size());
249     for (int i=0; i<expected.length; i++) {
250       if(PRINT){
251         System.out.println("expected "+expected[i]+
252             ", actual " +actual.get(i));
253       }
254       assertEquals(expected[i], actual.get(i));
255     }
256   }
257 
258 
259   /**
260    * Verify that {@link ScanQueryMatcher} only skips expired KeyValue
261    * instances and does not exit early from the row (skipping
262    * later non-expired KeyValues).  This version mimics a Get with
263    * wildcard-inferred column qualifiers.
264    *
265    * @throws IOException
266    */
267   public void testMatch_ExpiredWildcard()
268   throws IOException {
269 
270     long testTTL = 1000;
271     MatchCode [] expected = new MatchCode[] {
272         ScanQueryMatcher.MatchCode.INCLUDE,
273         ScanQueryMatcher.MatchCode.INCLUDE,
274         ScanQueryMatcher.MatchCode.SEEK_NEXT_COL,
275         ScanQueryMatcher.MatchCode.INCLUDE,
276         ScanQueryMatcher.MatchCode.SEEK_NEXT_COL,
277         ScanQueryMatcher.MatchCode.DONE
278     };
279 
280     long now = EnvironmentEdgeManager.currentTimeMillis();
281     ScanQueryMatcher qm = new ScanQueryMatcher(scan, new Store.ScanInfo(fam2,
282         0, 1, testTTL, false, 0, rowComparator), null,
283         now - testTTL);
284 
285     KeyValue [] kvs = new KeyValue[] {
286         new KeyValue(row1, fam2, col1, now-100, data),
287         new KeyValue(row1, fam2, col2, now-50, data),
288         new KeyValue(row1, fam2, col3, now-5000, data),
289         new KeyValue(row1, fam2, col4, now-500, data),
290         new KeyValue(row1, fam2, col5, now-10000, data),
291         new KeyValue(row2, fam1, col1, now-10, data)
292     };
293     KeyValue k = kvs[0];
294     qm.setRow(k.getBuffer(), k.getRowOffset(), k.getRowLength());
295 
296     List<ScanQueryMatcher.MatchCode> actual =
297         new ArrayList<ScanQueryMatcher.MatchCode>(kvs.length);
298     for (KeyValue kv : kvs) {
299       actual.add( qm.match(kv) );
300     }
301 
302     assertEquals(expected.length, actual.size());
303     for (int i=0; i<expected.length; i++) {
304       if(PRINT){
305         System.out.println("expected "+expected[i]+
306             ", actual " +actual.get(i));
307       }
308       assertEquals(expected[i], actual.get(i));
309     }
310   }
311 
312   @org.junit.Rule
313   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
314     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
315 }
316