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  
30  /**
31   * FilterListWithAND represents an ordered list of filters which will be evaluated with an AND
32   * operator.
33   */
34  @InterfaceAudience.Private
35  public class FilterListWithAND extends FilterListBase {
36  
37    private List<Filter> seekHintFilters = new ArrayList<>();
38  
39    public FilterListWithAND(List<Filter> filters) {
40      super(filters);
41      // For FilterList with AND, when call FL's transformCell(), we should transform cell for all
42      // sub-filters (because all sub-filters return INCLUDE*). So here, fill this array with true. we
43      // keep this in FilterListWithAND for abstracting the transformCell() in FilterListBase.
44      subFiltersIncludedCell = new ArrayList<>(Collections.nCopies(filters.size(), true));
45    }
46  
47    @Override
48    public void addFilterLists(List<Filter> filters) {
49      if (checkAndGetReversed(filters, isReversed()) != isReversed()) {
50        throw new IllegalArgumentException("Filters in the list must have the same reversed flag");
51      }
52      this.filters.addAll(filters);
53      this.subFiltersIncludedCell.addAll(Collections.nCopies(filters.size(), true));
54    }
55  
56    @Override
57    protected String formatLogFilters(List<Filter> logFilters) {
58      return String.format("FilterList AND (%d/%d): %s", logFilters.size(), this.size(),
59        logFilters.toString());
60    }
61  
62    /**
63     * FilterList with MUST_PASS_ALL choose the maximal forward step among sub-filters in filter list.
64     * Let's call it: The Maximal Step Rule. So if filter-A in filter list return INCLUDE and filter-B
65     * in filter list return INCLUDE_AND_NEXT_COL, then the filter list should return
66     * INCLUDE_AND_NEXT_COL. For SEEK_NEXT_USING_HINT, it's more special, and in method
67     * filterKeyValueWithMustPassAll(), if any sub-filter return SEEK_NEXT_USING_HINT, then our filter
68     * list will return SEEK_NEXT_USING_HINT. so we don't care about the SEEK_NEXT_USING_HINT here.
69     * <br/>
70     * <br/>
71     * The jump step will be:
72     *
73     * <pre>
74     * 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
75     * </pre>
76     *
77     * Here, we have the following map to describe The Maximal Step Rule. if current return code (for
78     * previous sub-filters in filter list) is <strong>ReturnCode</strong>, and current filter returns
79     * <strong>localRC</strong>, then we should return map[ReturnCode][localRC] for the merged result,
80     * according to The Maximal Step Rule. <br/>
81     *
82     * <pre>
83     * LocalCode\ReturnCode       INCLUDE                    INCLUDE_AND_NEXT_COL      INCLUDE_AND_SEEK_NEXT_ROW  SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
84     * INCLUDE                    INCLUDE                    INCLUDE_AND_NEXT_COL      INCLUDE_AND_SEEK_NEXT_ROW  SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
85     * 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
86     * 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
87     * SKIP                       SKIP                       NEXT_COL                  NEXT_ROW                   SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
88     * NEXT_COL                   NEXT_COL                   NEXT_COL                  NEXT_ROW                   NEXT_COL              NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
89     * NEXT_ROW                   NEXT_ROW                   NEXT_ROW                  NEXT_ROW                   NEXT_ROW              NEXT_ROW              NEXT_ROW              SEEK_NEXT_USING_HINT
90     * 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
91     * </pre>
92     *
93     * @param rc Return code which is calculated by previous sub-filter(s) in filter list.
94     * @param localRC Return code of the current sub-filter in filter list.
95     * @return Return code which is merged by the return code of previous sub-filter(s) and the return
96     *         code of current sub-filter.
97     */
98    private ReturnCode mergeReturnCode(ReturnCode rc, ReturnCode localRC) {
99      if (rc == ReturnCode.SEEK_NEXT_USING_HINT) {
100       return ReturnCode.SEEK_NEXT_USING_HINT;
101     }
102     switch (localRC) {
103       case SEEK_NEXT_USING_HINT:
104         return ReturnCode.SEEK_NEXT_USING_HINT;
105       case INCLUDE:
106         return rc;
107       case INCLUDE_AND_NEXT_COL:
108         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL)) {
109           return ReturnCode.INCLUDE_AND_NEXT_COL;
110         }
111         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW)) {
112           return ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW;
113         }
114         if (isInReturnCodes(rc, ReturnCode.SKIP, ReturnCode.NEXT_COL)) {
115           return ReturnCode.NEXT_COL;
116         }
117         if (isInReturnCodes(rc, ReturnCode.NEXT_ROW)) {
118           return ReturnCode.NEXT_ROW;
119         }
120         break;
121       case INCLUDE_AND_SEEK_NEXT_ROW:
122         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL,
123           ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW)) {
124           return ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW;
125         }
126         if (isInReturnCodes(rc, ReturnCode.SKIP, ReturnCode.NEXT_COL, ReturnCode.NEXT_ROW)) {
127           return ReturnCode.NEXT_ROW;
128         }
129         break;
130       case SKIP:
131         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.SKIP)) {
132           return ReturnCode.SKIP;
133         }
134         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_NEXT_COL, ReturnCode.NEXT_COL)) {
135           return ReturnCode.NEXT_COL;
136         }
137         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, ReturnCode.NEXT_ROW)) {
138           return ReturnCode.NEXT_ROW;
139         }
140         break;
141       case NEXT_COL:
142         if (isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL, ReturnCode.SKIP,
143           ReturnCode.NEXT_COL)) {
144           return ReturnCode.NEXT_COL;
145         }
146         if (isInReturnCodes(rc, ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, ReturnCode.NEXT_ROW)) {
147           return ReturnCode.NEXT_ROW;
148         }
149         break;
150       case NEXT_ROW:
151         return ReturnCode.NEXT_ROW;
152     }
153     throw new IllegalStateException(
154         "Received code is not valid. rc: " + rc + ", localRC: " + localRC);
155   }
156 
157   private boolean isIncludeRelatedReturnCode(ReturnCode rc) {
158     return isInReturnCodes(rc, ReturnCode.INCLUDE, ReturnCode.INCLUDE_AND_NEXT_COL,
159       ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW);
160   }
161 
162   @Override
163   public ReturnCode filterKeyValue(Cell c) throws IOException {
164     if (isEmpty()) {
165       return ReturnCode.INCLUDE;
166     }
167     ReturnCode rc = ReturnCode.INCLUDE;
168     this.seekHintFilters.clear();
169     for (int i = 0, n = filters.size(); i < n; i++) {
170       Filter filter = filters.get(i);
171       if (filter.filterAllRemaining()) {
172         return ReturnCode.NEXT_ROW;
173       }
174       ReturnCode localRC = filter.filterKeyValue(c);
175       if (localRC == ReturnCode.SEEK_NEXT_USING_HINT) {
176         seekHintFilters.add(filter);
177       }
178       rc = mergeReturnCode(rc, localRC);
179       // Only when rc is INCLUDE* case, we should pass the cell to the following sub-filters.
180       // otherwise we may mess up the global state (such as offset, count..) in the following
181       // sub-filters. (HBASE-20565)
182       if (!isIncludeRelatedReturnCode(rc)) {
183         return rc;
184       }
185     }
186     if (!seekHintFilters.isEmpty()) {
187       return ReturnCode.SEEK_NEXT_USING_HINT;
188     }
189     return rc;
190   }
191 
192   @Override
193   public void reset() throws IOException {
194     for (int i = 0, n = filters.size(); i < n; i++) {
195       filters.get(i).reset();
196     }
197     seekHintFilters.clear();
198   }
199 
200   @Override
201   public boolean filterRowKey(byte[] rowKey, int offset, int length) throws IOException {
202     if (isEmpty()) {
203       return super.filterRowKey(rowKey, offset, length);
204     }
205     boolean retVal = false;
206     for (int i = 0, n = filters.size(); i < n; i++) {
207       Filter filter = filters.get(i);
208       if (filter.filterAllRemaining() || filter.filterRowKey(rowKey, offset, length)) {
209         retVal = true;
210       }
211     }
212     return retVal;
213   }
214 
215   @Override
216   public boolean filterAllRemaining() throws IOException {
217     if (isEmpty()) {
218       return super.filterAllRemaining();
219     }
220     for (int i = 0, n = filters.size(); i < n; i++) {
221       if (filters.get(i).filterAllRemaining()) {
222         return true;
223       }
224     }
225     return false;
226   }
227 
228   @Override
229   public boolean filterRow() throws IOException {
230     if (isEmpty()) {
231       return super.filterRow();
232     }
233     for (int i = 0, n = filters.size(); i < n; i++) {
234       Filter filter = filters.get(i);
235       if (filter.filterRow()) {
236         return true;
237       }
238     }
239     return false;
240   }
241 
242   @Override
243   public Cell getNextCellHint(Cell currentCell) throws IOException {
244     if (isEmpty()) {
245       return super.getNextCellHint(currentCell);
246     }
247     Cell maxHint = null;
248     for (Filter filter : seekHintFilters) {
249       if (filter.filterAllRemaining()) {
250         continue;
251       }
252       Cell curKeyHint = filter.getNextCellHint(currentCell);
253       if (maxHint == null) {
254         maxHint = curKeyHint;
255         continue;
256       }
257       if (this.compareCell(maxHint, curKeyHint) < 0) {
258         maxHint = curKeyHint;
259       }
260     }
261     return maxHint;
262   }
263 }