001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020package org.apache.hadoop.hbase.filter;
021
022import org.apache.hadoop.hbase.Cell;
023import org.apache.yetus.audience.InterfaceAudience;
024
025import java.io.IOException;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.List;
029
030/**
031 * FilterListWithAND represents an ordered list of filters which will be evaluated with an AND
032 * operator.
033 */
034@InterfaceAudience.Private
035public class FilterListWithAND extends FilterListBase {
036
037  private List<Filter> seekHintFilters = new ArrayList<>();
038
039  public FilterListWithAND(List<Filter> filters) {
040    super(filters);
041    // For FilterList with AND, when call FL's transformCell(), we should transform cell for all
042    // sub-filters (because all sub-filters return INCLUDE*). So here, fill this array with true. we
043    // keep this in FilterListWithAND for abstracting the transformCell() in FilterListBase.
044    subFiltersIncludedCell = new ArrayList<>(Collections.nCopies(filters.size(), true));
045  }
046
047  @Override
048  public void addFilterLists(List<Filter> filters) {
049    if (checkAndGetReversed(filters, isReversed()) != isReversed()) {
050      throw new IllegalArgumentException("Filters in the list must have the same reversed flag");
051    }
052    this.filters.addAll(filters);
053    this.subFiltersIncludedCell.addAll(Collections.nCopies(filters.size(), true));
054  }
055
056  @Override
057  protected String formatLogFilters(List<Filter> logFilters) {
058    return String.format("FilterList AND (%d/%d): %s", logFilters.size(), this.size(),
059      logFilters.toString());
060  }
061
062  /**
063   * FilterList with MUST_PASS_ALL choose the maximal forward step among sub-filters in filter list.
064   * Let's call it: The Maximal Step Rule. So if filter-A in filter list return INCLUDE and filter-B
065   * in filter list return INCLUDE_AND_NEXT_COL, then the filter list should return
066   * INCLUDE_AND_NEXT_COL. For SEEK_NEXT_USING_HINT, it's more special, and in method
067   * filterCellWithMustPassAll(), if any sub-filter return SEEK_NEXT_USING_HINT, then our filter
068   * list will return SEEK_NEXT_USING_HINT. so we don't care about the SEEK_NEXT_USING_HINT here.
069   * <br/>
070   * <br/>
071   * The jump step will be:
072   *
073   * <pre>
074   * 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
075   * </pre>
076   *
077   * Here, we have the following map to describe The Maximal Step Rule. if current return code (for
078   * previous sub-filters in filter list) is <strong>ReturnCode</strong>, and current filter returns
079   * <strong>localRC</strong>, then we should return map[ReturnCode][localRC] for the merged result,
080   * according to The Maximal Step Rule. <br/>
081   *
082   * <pre>
083   * LocalCode\ReturnCode       INCLUDE                    INCLUDE_AND_NEXT_COL      INCLUDE_AND_SEEK_NEXT_ROW  SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
084   * INCLUDE                    INCLUDE                    INCLUDE_AND_NEXT_COL      INCLUDE_AND_SEEK_NEXT_ROW  SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
085   * 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
086   * 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
087   * SKIP                       SKIP                       NEXT_COL                  NEXT_ROW                   SKIP                  NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
088   * NEXT_COL                   NEXT_COL                   NEXT_COL                  NEXT_ROW                   NEXT_COL              NEXT_COL              NEXT_ROW              SEEK_NEXT_USING_HINT
089   * NEXT_ROW                   NEXT_ROW                   NEXT_ROW                  NEXT_ROW                   NEXT_ROW              NEXT_ROW              NEXT_ROW              SEEK_NEXT_USING_HINT
090   * 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
091   * </pre>
092   *
093   * @param rc Return code which is calculated by previous sub-filter(s) in filter list.
094   * @param localRC Return code of the current sub-filter in filter list.
095   * @return Return code which is merged by the return code of previous sub-filter(s) and the return
096   *         code of current sub-filter.
097   */
098  private ReturnCode mergeReturnCode(ReturnCode rc, ReturnCode localRC) {
099    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 filterCell(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;
175      localRC = filter.filterCell(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 filterRowKey(Cell firstRowCell) throws IOException {
218    if (isEmpty()) {
219      return super.filterRowKey(firstRowCell);
220    }
221    boolean retVal = false;
222    for (int i = 0, n = filters.size(); i < n; i++) {
223      Filter filter = filters.get(i);
224      if (filter.filterAllRemaining() || filter.filterRowKey(firstRowCell)) {
225        // Can't just return true here, because there are some filters (such as PrefixFilter) which
226        // will catch the row changed event by filterRowKey(). If we return early here, those
227        // filters will have no chance to update their row state.
228        retVal = true;
229      }
230    }
231    return retVal;
232  }
233
234  @Override
235  public boolean filterAllRemaining() throws IOException {
236    if (isEmpty()) {
237      return super.filterAllRemaining();
238    }
239    for (int i = 0, n = filters.size(); i < n; i++) {
240      if (filters.get(i).filterAllRemaining()) {
241        return true;
242      }
243    }
244    return false;
245  }
246
247  @Override
248  public boolean filterRow() throws IOException {
249    if (isEmpty()) {
250      return super.filterRow();
251    }
252    for (int i = 0, n = filters.size(); i < n; i++) {
253      Filter filter = filters.get(i);
254      if (filter.filterRow()) {
255        return true;
256      }
257    }
258    return false;
259  }
260
261  @Override
262  public Cell getNextCellHint(Cell currentCell) throws IOException {
263    if (isEmpty()) {
264      return super.getNextCellHint(currentCell);
265    }
266    Cell maxHint = null;
267    for (Filter filter : seekHintFilters) {
268      if (filter.filterAllRemaining()) {
269        continue;
270      }
271      Cell curKeyHint = filter.getNextCellHint(currentCell);
272      if (maxHint == null) {
273        maxHint = curKeyHint;
274        continue;
275      }
276      if (this.compareCell(maxHint, curKeyHint) < 0) {
277        maxHint = curKeyHint;
278      }
279    }
280    return maxHint;
281  }
282}