View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.filter;
21  
22  import org.apache.hadoop.hbase.Cell;
23  import org.apache.hadoop.hbase.classification.InterfaceAudience;
24  
25  import java.io.IOException;
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.List;
29  import java.util.Objects;
30  
31  /**
32   * FilterListWithAND represents an ordered list of filters which will be evaluated with an AND
33   * operator.
34   */
35  @InterfaceAudience.Private
36  public class FilterListWithAND extends FilterListBase {
37  
38    private List<Filter> seekHintFilters = new ArrayList<>();
39  
40    public FilterListWithAND(List<Filter> filters) {
41      super(filters);
42      // For FilterList with AND, when call FL's transformCell(), we should transform cell for all
43      // sub-filters (because all sub-filters return INCLUDE*). So here, fill this array with true. we
44      // keep this in FilterListWithAND for abstracting the transformCell() in FilterListBase.
45      subFiltersIncludedCell = new ArrayList<>(Collections.nCopies(filters.size(), true));
46    }
47  
48    @Override
49    public void addFilterLists(List<Filter> filters) {
50      if (checkAndGetReversed(filters, isReversed()) != isReversed()) {
51        throw new IllegalArgumentException("Filters in the list must have the same reversed flag");
52      }
53      this.filters.addAll(filters);
54      this.subFiltersIncludedCell.addAll(Collections.nCopies(filters.size(), true));
55    }
56  
57    @Override
58    protected String formatLogFilters(List<Filter> logFilters) {
59      return String.format("FilterList AND (%d/%d): %s", logFilters.size(), this.size(),
60        logFilters.toString());
61    }
62  
63    /**
64     * FilterList with MUST_PASS_ALL choose the maximal forward step among sub-filters in filter list.
65     * Let's call it: The Maximal Step Rule. So if filter-A in filter list return INCLUDE and filter-B
66     * in filter list return INCLUDE_AND_NEXT_COL, then the filter list should return
67     * INCLUDE_AND_NEXT_COL. For SEEK_NEXT_USING_HINT, it's more special, and in method
68     * filterKeyValueWithMustPassAll(), if any sub-filter return SEEK_NEXT_USING_HINT, then our filter
69     * list will return SEEK_NEXT_USING_HINT. so we don't care about the SEEK_NEXT_USING_HINT here.
70     * <br/>
71     * <br/>
72     * The jump step will be:
73     *
74     * <pre>
75     * INCLUDE &lt; SKIP &lt; INCLUDE_AND_NEXT_COL &lt; NEXT_COL &lt; INCLUDE_AND_SEEK_NEXT_ROW &lt; NEXT_ROW &lt; SEEK_NEXT_USING_HINT
76     * </pre>
77     *
78     * Here, we have the following map to describe The Maximal Step Rule. if current return code (for
79     * previous sub-filters in filter list) is <strong>ReturnCode</strong>, and current filter returns
80     * <strong>localRC</strong>, then we should return map[ReturnCode][localRC] for the merged result,
81     * according to The Maximal Step Rule. <br/>
82     *
83     * <pre>
84     * LocalCode\ReturnCode       INCLUDE                    INCLUDE_AND_NEXT_COL      INCLUDE_AND_SEEK_NEXT_ROW  SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
85     * INCLUDE                    INCLUDE                    INCLUDE_AND_NEXT_COL      INCLUDE_AND_SEEK_NEXT_ROW  SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
86     * INCLUDE_AND_NEXT_COL       INCLUDE_AND_NEXT_COL       INCLUDE_AND_NEXT_COL      INCLUDE_AND_SEEK_NEXT_ROW  NEXT_COL              NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
87     * INCLUDE_AND_SEEK_NEXT_ROW  INCLUDE_AND_SEEK_NEXT_ROW  INCLUDE_AND_SEEK_NEXT_ROW INCLUDE_AND_SEEK_NEXT_ROW  NEXT_ROW              NEXT_ROW              NEXT_ROW              SEEK_NEXT_USING_HINT
88     * SKIP                       SKIP                       NEXT_COL                  NEXT_ROW                   SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
89     * NEXT_COL                   NEXT_COL                   NEXT_COL                  NEXT_ROW                   NEXT_COL              NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
90     * NEXT_ROW                   NEXT_ROW                   NEXT_ROW                  NEXT_ROW                   NEXT_ROW              NEXT_ROW              NEXT_ROW              SEEK_NEXT_USING_HINT
91     * SEEK_NEXT_USING_HINT       SEEK_NEXT_USING_HINT       SEEK_NEXT_USING_HINT      SEEK_NEXT_USING_HINT       SEEK_NEXT_USING_HINT  SEEK_NEXT_USING_HINT  SEEK_NEXT_USING_HINT  SEEK_NEXT_USING_HINT
92     * </pre>
93     *
94     * @param rc Return code which is calculated by previous sub-filter(s) in filter list.
95     * @param localRC Return code of the current sub-filter in filter list.
96     * @return Return code which is merged by the return code of previous sub-filter(s) and the return
97     *         code of current sub-filter.
98     */
99    private ReturnCode mergeReturnCode(ReturnCode rc, ReturnCode localRC) {
100     if (rc == ReturnCode.SEEK_NEXT_USING_HINT) {
101       return ReturnCode.SEEK_NEXT_USING_HINT;
102     }
103     switch (localRC) {
104       case SEEK_NEXT_USING_HINT:
105         return ReturnCode.SEEK_NEXT_USING_HINT;
106       case INCLUDE:
107         return rc;
108       case INCLUDE_AND_NEXT_COL:
109         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL)) {
110           return ReturnCode.INCLUDE_AND_NEXT_COL;
111         }
112         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW)) {
113           return ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW;
114         }
115         if (isInReturnCodes(rc, ReturnCode.SKIP, ReturnCode.NEXT_COL)) {
116           return ReturnCode.NEXT_COL;
117         }
118         if (isInReturnCodes(rc, ReturnCode.NEXT_ROW)) {
119           return ReturnCode.NEXT_ROW;
120         }
121         break;
122       case INCLUDE_AND_SEEK_NEXT_ROW:
123         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL,
124           ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW)) {
125           return ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW;
126         }
127         if (isInReturnCodes(rc, ReturnCode.SKIP, ReturnCode.NEXT_COL, ReturnCode.NEXT_ROW)) {
128           return ReturnCode.NEXT_ROW;
129         }
130         break;
131       case SKIP:
132         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.SKIP)) {
133           return ReturnCode.SKIP;
134         }
135         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_NEXT_COL, ReturnCode.NEXT_COL)) {
136           return ReturnCode.NEXT_COL;
137         }
138         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, ReturnCode.NEXT_ROW)) {
139           return ReturnCode.NEXT_ROW;
140         }
141         break;
142       case NEXT_COL:
143         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL, ReturnCode.SKIP,
144           ReturnCode.NEXT_COL)) {
145           return ReturnCode.NEXT_COL;
146         }
147         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, ReturnCode.NEXT_ROW)) {
148           return ReturnCode.NEXT_ROW;
149         }
150         break;
151       case NEXT_ROW:
152         return ReturnCode.NEXT_ROW;
153     }
154     throw new IllegalStateException(
155         "Received code is not valid. rc: " + rc + ", localRC: " + localRC);
156   }
157 
158   private boolean isIncludeRelatedReturnCode(ReturnCode rc) {
159     return isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL,
160       ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW);
161   }
162 
163   @Override
164   public ReturnCode filterKeyValue(Cell c) throws IOException {
165     if (isEmpty()) {
166       return ReturnCode.INCLUDE;
167     }
168     ReturnCode rc = ReturnCode.INCLUDE;
169     this.seekHintFilters.clear();
170     for (int i = 0, n = filters.size(); i < n; i++) {
171       Filter filter = filters.get(i);
172       if (filter.filterAllRemaining()) {
173         return ReturnCode.NEXT_ROW;
174       }
175       ReturnCode localRC = filter.filterKeyValue(c);
176       if (localRC == ReturnCode.SEEK_NEXT_USING_HINT) {
177         seekHintFilters.add(filter);
178       }
179       rc = mergeReturnCode(rc, localRC);
180       // Only when rc is INCLUDE* case, we should pass the cell to the following sub-filters.
181       // otherwise we may mess up the global state (such as offset, count..) in the following
182       // sub-filters. (HBASE-20565)
183       if (!isIncludeRelatedReturnCode(rc)) {
184         return rc;
185       }
186     }
187     if (!seekHintFilters.isEmpty()) {
188       return ReturnCode.SEEK_NEXT_USING_HINT;
189     }
190     return rc;
191   }
192 
193   @Override
194   public void reset() throws IOException {
195     for (int i = 0, n = filters.size(); i < n; i++) {
196       filters.get(i).reset();
197     }
198     seekHintFilters.clear();
199   }
200 
201   @Override
202   public boolean filterRowKey(byte[] rowKey, int offset, int length) throws IOException {
203     if (isEmpty()) {
204       return super.filterRowKey(rowKey, offset, length);
205     }
206     boolean retVal = false;
207     for (int i = 0, n = filters.size(); i < n; i++) {
208       Filter filter = filters.get(i);
209       if (filter.filterAllRemaining() || filter.filterRowKey(rowKey, offset, length)) {
210         retVal = true;
211       }
212     }
213     return retVal;
214   }
215 
216   @Override
217   public boolean filterAllRemaining() throws IOException {
218     if (isEmpty()) {
219       return super.filterAllRemaining();
220     }
221     for (int i = 0, n = filters.size(); i < n; i++) {
222       if (filters.get(i).filterAllRemaining()) {
223         return true;
224       }
225     }
226     return false;
227   }
228 
229   @Override
230   public boolean filterRow() throws IOException {
231     if (isEmpty()) {
232       return super.filterRow();
233     }
234     for (int i = 0, n = filters.size(); i < n; i++) {
235       Filter filter = filters.get(i);
236       if (filter.filterRow()) {
237         return true;
238       }
239     }
240     return false;
241   }
242 
243   @Override
244   public Cell getNextCellHint(Cell currentCell) throws IOException {
245     if (isEmpty()) {
246       return super.getNextCellHint(currentCell);
247     }
248     Cell maxHint = null;
249     for (Filter filter : seekHintFilters) {
250       if (filter.filterAllRemaining()) {
251         continue;
252       }
253       Cell curKeyHint = filter.getNextCellHint(currentCell);
254       if (maxHint == null) {
255         maxHint = curKeyHint;
256         continue;
257       }
258       if (this.compareCell(maxHint, curKeyHint) < 0) {
259         maxHint = curKeyHint;
260       }
261     }
262     return maxHint;
263   }
264 
265   @Override
266   public boolean equals(Object obj) {
267     if (!(obj instanceof FilterListWithAND)) {
268       return false;
269     }
270     if (this == obj) {
271       return true;
272     }
273     FilterListWithAND f = (FilterListWithAND) obj;
274     return this.filters.equals(f.getFilters()) && this.seekHintFilters.equals(f.seekHintFilters);
275   }
276 
277   @Override
278   public int hashCode() {
279     return Objects.hash(this.seekHintFilters, this.filters);
280   }
281 }