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 < SKIP < INCLUDE_AND_NEXT_COL < NEXT_COL < INCLUDE_AND_SEEK_NEXT_ROW < NEXT_ROW < 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}