1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.filter;
19
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Objects;
25
26 import org.apache.hadoop.hbase.Cell;
27 import org.apache.hadoop.hbase.HConstants;
28 import org.apache.hadoop.hbase.KeyValueUtil;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.hbase.classification.InterfaceStability;
31 import org.apache.hadoop.hbase.client.ClientUtil;
32 import org.apache.hadoop.hbase.exceptions.DeserializationException;
33 import org.apache.hadoop.hbase.protobuf.generated.FilterProtos;
34 import org.apache.hadoop.hbase.util.ByteStringer;
35 import org.apache.hadoop.hbase.util.Bytes;
36
37 import com.google.protobuf.InvalidProtocolBufferException;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 @InterfaceAudience.Public
55 @InterfaceStability.Evolving
56 public class MultiRowRangeFilter extends FilterBase {
57
58 private static final int ROW_BEFORE_FIRST_RANGE = -1;
59
60 private final List<RowRange> rangeList;
61 private final RangeIteration ranges;
62
63 private boolean done = false;
64 private int index;
65 private BasicRowRange range;
66 private ReturnCode currentReturnCode;
67
68
69
70
71
72
73
74 public MultiRowRangeFilter(List<RowRange> list) throws IOException {
75
76
77 this.rangeList = Collections.unmodifiableList(sortAndMerge(list));
78 this.ranges = new RangeIteration(rangeList);
79 }
80
81
82
83
84
85
86
87
88
89
90
91 public MultiRowRangeFilter(byte[][] rowKeyPrefixes) throws IOException {
92 this(createRangeListFromRowKeyPrefixes(rowKeyPrefixes));
93 }
94
95 private static List<RowRange> createRangeListFromRowKeyPrefixes(byte[][] rowKeyPrefixes) {
96 if (rowKeyPrefixes == null) {
97 throw new IllegalArgumentException("Invalid rowkey prefixes");
98 }
99
100 List<RowRange> list = new ArrayList<>();
101 for (byte[] rowKeyPrefix: rowKeyPrefixes) {
102 byte[] stopRow = ClientUtil.calculateTheClosestNextRowKeyForPrefix(rowKeyPrefix);
103 list.add(new RowRange(rowKeyPrefix, true, stopRow, false));
104 }
105 return list;
106 }
107
108 public List<RowRange> getRowRanges() {
109
110 return this.rangeList;
111 }
112
113 @Override
114 public boolean filterAllRemaining() {
115 return done;
116 }
117
118 @Override
119 public boolean filterRowKey(byte[] buffer, int offset, int length) {
120
121
122
123 if (!ranges.isInitialized()) {
124 ranges.initialize(isReversed());
125 }
126
127
128
129
130
131 if (!ranges.hasFoundFirstRange() || !range.contains(buffer, offset, length)) {
132 byte[] rowkey = new byte[length];
133 System.arraycopy(buffer, offset, rowkey, 0, length);
134 index = ranges.getNextRangeIndex(rowkey);
135 if (ranges.isIterationComplete(index)) {
136 done = true;
137 currentReturnCode = ReturnCode.NEXT_ROW;
138 return false;
139 }
140 if(index != ROW_BEFORE_FIRST_RANGE) {
141 range = ranges.get(index);
142 } else {
143 range = ranges.get(0);
144 }
145 if (ranges.isExclusive()) {
146 ranges.resetExclusive();
147 currentReturnCode = ReturnCode.NEXT_ROW;
148 return false;
149 }
150 if (!ranges.hasFoundFirstRange()) {
151 if(index != ROW_BEFORE_FIRST_RANGE) {
152 currentReturnCode = ReturnCode.INCLUDE;
153 } else {
154 currentReturnCode = ReturnCode.SEEK_NEXT_USING_HINT;
155 }
156 ranges.setFoundFirstRange();
157 } else {
158 if (range.contains(buffer, offset, length)) {
159 currentReturnCode = ReturnCode.INCLUDE;
160 } else {
161 currentReturnCode = ReturnCode.SEEK_NEXT_USING_HINT;
162 }
163 }
164 } else {
165 currentReturnCode = ReturnCode.INCLUDE;
166 }
167 return false;
168 }
169
170 @Override
171 public ReturnCode filterKeyValue(Cell ignored) {
172 return currentReturnCode;
173 }
174
175 @Override
176 public Cell getNextCellHint(Cell currentKV) {
177
178
179 byte[] comparisonData = range.getComparisonData();
180 return KeyValueUtil.createFirstOnRow(comparisonData);
181 }
182
183
184
185
186 @Override
187 public byte[] toByteArray() {
188 FilterProtos.MultiRowRangeFilter.Builder builder = FilterProtos.MultiRowRangeFilter
189 .newBuilder();
190 for (RowRange range : rangeList) {
191 if (range != null) {
192 FilterProtos.RowRange.Builder rangebuilder = FilterProtos.RowRange.newBuilder();
193 if (range.startRow != null)
194 rangebuilder.setStartRow(ByteStringer.wrap(range.startRow));
195 rangebuilder.setStartRowInclusive(range.startRowInclusive);
196 if (range.stopRow != null)
197 rangebuilder.setStopRow(ByteStringer.wrap(range.stopRow));
198 rangebuilder.setStopRowInclusive(range.stopRowInclusive);
199 builder.addRowRangeList(rangebuilder.build());
200 }
201 }
202 return builder.build().toByteArray();
203 }
204
205
206
207
208
209
210 public static MultiRowRangeFilter parseFrom(final byte[] pbBytes)
211 throws DeserializationException {
212 FilterProtos.MultiRowRangeFilter proto;
213 try {
214 proto = FilterProtos.MultiRowRangeFilter.parseFrom(pbBytes);
215 } catch (InvalidProtocolBufferException e) {
216 throw new DeserializationException(e);
217 }
218 int length = proto.getRowRangeListCount();
219 List<FilterProtos.RowRange> rangeProtos = proto.getRowRangeListList();
220 List<RowRange> rangeList = new ArrayList<RowRange>(length);
221 for (FilterProtos.RowRange rangeProto : rangeProtos) {
222 RowRange range = new RowRange(rangeProto.hasStartRow() ? rangeProto.getStartRow()
223 .toByteArray() : null, rangeProto.getStartRowInclusive(), rangeProto.hasStopRow() ?
224 rangeProto.getStopRow().toByteArray() : null, rangeProto.getStopRowInclusive());
225 rangeList.add(range);
226 }
227 try {
228 return new MultiRowRangeFilter(rangeList);
229 } catch (IOException e) {
230 throw new DeserializationException("Fail to instantiate the MultiRowRangeFilter", e);
231 }
232 }
233
234
235
236
237
238
239 @Override
240 boolean areSerializedFieldsEqual(Filter o) {
241 if (o == this)
242 return true;
243 if (!(o instanceof MultiRowRangeFilter))
244 return false;
245
246 MultiRowRangeFilter other = (MultiRowRangeFilter) o;
247 if (this.rangeList.size() != other.rangeList.size())
248 return false;
249 for (int i = 0; i < rangeList.size(); ++i) {
250 RowRange thisRange = this.rangeList.get(i);
251 RowRange otherRange = other.rangeList.get(i);
252 if (!(Bytes.equals(thisRange.startRow, otherRange.startRow) && Bytes.equals(
253 thisRange.stopRow, otherRange.stopRow) && (thisRange.startRowInclusive ==
254 otherRange.startRowInclusive) && (thisRange.stopRowInclusive ==
255 otherRange.stopRowInclusive))) {
256 return false;
257 }
258 }
259 return true;
260 }
261
262
263
264
265
266
267
268 public static List<RowRange> sortAndMerge(List<RowRange> ranges) {
269 if (ranges.size() == 0) {
270 throw new IllegalArgumentException("No ranges found.");
271 }
272 List<RowRange> invalidRanges = new ArrayList<RowRange>();
273 List<RowRange> newRanges = new ArrayList<RowRange>(ranges.size());
274 Collections.sort(ranges);
275 if(ranges.get(0).isValid()) {
276 if (ranges.size() == 1) {
277 newRanges.add(ranges.get(0));
278 }
279 } else {
280 invalidRanges.add(ranges.get(0));
281 }
282
283 byte[] lastStartRow = ranges.get(0).startRow;
284 boolean lastStartRowInclusive = ranges.get(0).startRowInclusive;
285 byte[] lastStopRow = ranges.get(0).stopRow;
286 boolean lastStopRowInclusive = ranges.get(0).stopRowInclusive;
287 int i = 1;
288 for (; i < ranges.size(); i++) {
289 RowRange range = ranges.get(i);
290 if (!range.isValid()) {
291 invalidRanges.add(range);
292 }
293 if(Bytes.equals(lastStopRow, HConstants.EMPTY_BYTE_ARRAY)) {
294 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, lastStopRow,
295 lastStopRowInclusive));
296 break;
297 }
298
299 if ((Bytes.compareTo(lastStopRow, range.startRow) > 0) ||
300 (Bytes.compareTo(lastStopRow, range.startRow) == 0 && !(lastStopRowInclusive == false &&
301 range.isStartRowInclusive() == false))) {
302 if(Bytes.equals(range.stopRow, HConstants.EMPTY_BYTE_ARRAY)) {
303 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, range.stopRow,
304 range.stopRowInclusive));
305 break;
306 }
307
308 if (Bytes.compareTo(lastStopRow, range.stopRow) >= 0) {
309 if((Bytes.compareTo(lastStopRow, range.stopRow) == 0)) {
310 if(lastStopRowInclusive == true || range.stopRowInclusive == true) {
311 lastStopRowInclusive = true;
312 }
313 }
314 if ((i + 1) == ranges.size()) {
315 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, lastStopRow,
316 lastStopRowInclusive));
317 }
318 } else {
319 lastStopRow = range.stopRow;
320 lastStopRowInclusive = range.stopRowInclusive;
321 if ((i + 1) < ranges.size()) {
322 i++;
323 range = ranges.get(i);
324 if (!range.isValid()) {
325 invalidRanges.add(range);
326 }
327 } else {
328 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, lastStopRow,
329 lastStopRowInclusive));
330 break;
331 }
332 while ((Bytes.compareTo(lastStopRow, range.startRow) > 0) ||
333 (Bytes.compareTo(lastStopRow, range.startRow) == 0 &&
334 (lastStopRowInclusive == true || range.startRowInclusive==true))) {
335 if(Bytes.equals(range.stopRow, HConstants.EMPTY_BYTE_ARRAY)) {
336 break;
337 }
338
339 if (Bytes.compareTo(lastStopRow, range.stopRow) >= 0) {
340 if(lastStopRowInclusive == true || range.stopRowInclusive == true) {
341 lastStopRowInclusive = true;
342 }
343 i++;
344 if (i < ranges.size()) {
345 range = ranges.get(i);
346 if (!range.isValid()) {
347 invalidRanges.add(range);
348 }
349 } else {
350 break;
351 }
352 } else {
353 lastStopRow = range.stopRow;
354 lastStopRowInclusive = range.stopRowInclusive;
355 i++;
356 if (i < ranges.size()) {
357 range = ranges.get(i);
358 if (!range.isValid()) {
359 invalidRanges.add(range);
360 }
361 } else {
362 break;
363 }
364 }
365 }
366 if(Bytes.equals(range.stopRow, HConstants.EMPTY_BYTE_ARRAY)) {
367 if((Bytes.compareTo(lastStopRow, range.startRow) < 0) ||
368 (Bytes.compareTo(lastStopRow, range.startRow) == 0 &&
369 lastStopRowInclusive == false && range.startRowInclusive == false)) {
370 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, lastStopRow,
371 lastStopRowInclusive));
372 newRanges.add(range);
373 } else {
374 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, range.stopRow,
375 range.stopRowInclusive));
376 break;
377 }
378 }
379 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, lastStopRow,
380 lastStopRowInclusive));
381 if ((i + 1) == ranges.size()) {
382 newRanges.add(range);
383 }
384 lastStartRow = range.startRow;
385 lastStartRowInclusive = range.startRowInclusive;
386 lastStopRow = range.stopRow;
387 lastStopRowInclusive = range.stopRowInclusive;
388 }
389 } else {
390 newRanges.add(new RowRange(lastStartRow, lastStartRowInclusive, lastStopRow,
391 lastStopRowInclusive));
392 if ((i + 1) == ranges.size()) {
393 newRanges.add(range);
394 }
395 lastStartRow = range.startRow;
396 lastStartRowInclusive = range.startRowInclusive;
397 lastStopRow = range.stopRow;
398 lastStopRowInclusive = range.stopRowInclusive;
399 }
400 }
401
402 for(int j=i; j < ranges.size(); j++) {
403 if(!ranges.get(j).isValid()) {
404 invalidRanges.add(ranges.get(j));
405 }
406 }
407
408 if (invalidRanges.size() != 0) {
409 throwExceptionForInvalidRanges(invalidRanges, true);
410 }
411
412 if(newRanges.size() == 0) {
413 throw new IllegalArgumentException("No valid ranges found.");
414 }
415 return newRanges;
416 }
417
418 private static void throwExceptionForInvalidRanges(List<RowRange> invalidRanges,
419 boolean details) {
420 StringBuilder sb = new StringBuilder();
421 sb.append(invalidRanges.size()).append(" invaild ranges.\n");
422 if (details) {
423 for (RowRange range : invalidRanges) {
424 sb.append(
425 "Invalid range: start row => " + Bytes.toString(range.startRow) + ", stop row => "
426 + Bytes.toString(range.stopRow)).append('\n');
427 }
428 }
429 throw new IllegalArgumentException(sb.toString());
430 }
431
432 private static abstract class BasicRowRange {
433 protected byte[] startRow;
434 protected boolean startRowInclusive = true;
435 protected byte[] stopRow;
436 protected boolean stopRowInclusive = false;
437
438 public BasicRowRange() {
439 }
440
441
442
443
444
445 public BasicRowRange(String startRow, boolean startRowInclusive, String stopRow,
446 boolean stopRowInclusive) {
447 this((startRow == null || startRow.isEmpty()) ? HConstants.EMPTY_BYTE_ARRAY :
448 Bytes.toBytes(startRow), startRowInclusive,
449 (stopRow == null || stopRow.isEmpty()) ? HConstants.EMPTY_BYTE_ARRAY :
450 Bytes.toBytes(stopRow), stopRowInclusive);
451 }
452
453 public BasicRowRange(byte[] startRow, boolean startRowInclusive, byte[] stopRow,
454 boolean stopRowInclusive) {
455 this.startRow = (startRow == null) ? HConstants.EMPTY_BYTE_ARRAY : startRow;
456 this.startRowInclusive = startRowInclusive;
457 this.stopRow = (stopRow == null) ? HConstants.EMPTY_BYTE_ARRAY :stopRow;
458 this.stopRowInclusive = stopRowInclusive;
459 }
460
461 public byte[] getStartRow() {
462 return startRow;
463 }
464
465 public byte[] getStopRow() {
466 return stopRow;
467 }
468
469
470
471
472 public boolean isStartRowInclusive() {
473 return startRowInclusive;
474 }
475
476
477
478
479 public boolean isStopRowInclusive() {
480 return stopRowInclusive;
481 }
482
483 public boolean contains(byte[] row) {
484 return contains(row, 0, row.length);
485 }
486
487 public boolean contains(byte[] buffer, int offset, int length) {
488 if(startRowInclusive) {
489 if(stopRowInclusive) {
490 return Bytes.compareTo(buffer, offset, length, startRow, 0, startRow.length) >= 0
491 && (Bytes.equals(stopRow, HConstants.EMPTY_BYTE_ARRAY) ||
492 Bytes.compareTo(buffer, offset, length, stopRow, 0, stopRow.length) <= 0);
493 } else {
494 return Bytes.compareTo(buffer, offset, length, startRow, 0, startRow.length) >= 0
495 && (Bytes.equals(stopRow, HConstants.EMPTY_BYTE_ARRAY) ||
496 Bytes.compareTo(buffer, offset, length, stopRow, 0, stopRow.length) < 0);
497 }
498 } else {
499 if(stopRowInclusive) {
500 return Bytes.compareTo(buffer, offset, length, startRow, 0, startRow.length) > 0
501 && (Bytes.equals(stopRow, HConstants.EMPTY_BYTE_ARRAY) ||
502 Bytes.compareTo(buffer, offset, length, stopRow, 0, stopRow.length) <= 0);
503 } else {
504 return Bytes.compareTo(buffer, offset, length, startRow, 0, startRow.length) > 0
505 && (Bytes.equals(stopRow, HConstants.EMPTY_BYTE_ARRAY) ||
506 Bytes.compareTo(buffer, offset, length, stopRow, 0, stopRow.length) < 0);
507 }
508 }
509 }
510
511 public boolean isValid() {
512 return Bytes.equals(startRow, HConstants.EMPTY_BYTE_ARRAY)
513 || Bytes.equals(stopRow, HConstants.EMPTY_BYTE_ARRAY)
514 || Bytes.compareTo(startRow, stopRow) < 0
515 || (Bytes.compareTo(startRow, stopRow) == 0 && stopRowInclusive == true);
516 }
517
518 @Override
519 public boolean equals(Object obj){
520 if (!(obj instanceof BasicRowRange)) {
521 return false;
522 }
523 if (this == obj) {
524 return true;
525 }
526 BasicRowRange rr = (BasicRowRange) obj;
527 return Bytes.equals(this.stopRow, rr.getStopRow()) &&
528 Bytes.equals(this.startRow, this.getStartRow()) &&
529 this.startRowInclusive == rr.isStartRowInclusive() &&
530 this.stopRowInclusive == rr.isStopRowInclusive();
531 }
532
533 @Override
534 public int hashCode() {
535 return Objects.hash(Bytes.hashCode(this.stopRow),
536 Bytes.hashCode(this.startRow),
537 this.startRowInclusive,
538 this.stopRowInclusive);
539 }
540
541
542
543
544 public abstract byte[] getComparisonData();
545
546
547
548
549
550
551
552 public abstract boolean isSearchRowInclusive();
553
554 public abstract boolean isAscendingOrder();
555 }
556
557
558
559
560 @InterfaceAudience.Private
561 private static class ReversedRowRange extends BasicRowRange implements Comparable<ReversedRowRange> {
562 public ReversedRowRange(byte[] startRow, boolean startRowInclusive, byte[] stopRow,
563 boolean stopRowInclusive) {
564 super(startRow, startRowInclusive, stopRow, stopRowInclusive);
565 }
566
567 @Override
568 public byte[] getComparisonData() {
569 return this.stopRow;
570 }
571
572 @Override
573 public boolean isSearchRowInclusive() {
574 return this.stopRowInclusive;
575 }
576
577 @Override
578 public boolean isAscendingOrder() {
579 return false;
580 }
581
582 @Override
583 public int compareTo(ReversedRowRange other) {
584 byte[] left = other.getComparisonData();
585 byte[] right = this.getComparisonData();
586 return Bytes.compareTo(left, right);
587 }
588 }
589
590 @InterfaceAudience.Public
591 @InterfaceStability.Evolving
592 public static class RowRange extends BasicRowRange implements Comparable<RowRange> {
593 public RowRange() {
594 }
595
596
597
598
599
600 public RowRange(String startRow, boolean startRowInclusive, String stopRow,
601 boolean stopRowInclusive) {
602 super(startRow, startRowInclusive, stopRow, stopRowInclusive);
603 }
604
605 public RowRange(byte[] startRow, boolean startRowInclusive, byte[] stopRow,
606 boolean stopRowInclusive) {
607 super(startRow, startRowInclusive, stopRow, stopRowInclusive);
608 }
609
610 @Override
611 public byte[] getComparisonData() {
612 return startRow;
613 }
614
615 @Override
616 public boolean isSearchRowInclusive() {
617 return startRowInclusive;
618 }
619
620 @Override
621 public boolean isAscendingOrder() {
622 return true;
623 }
624
625 @Override
626 public int compareTo(RowRange other) {
627 byte[] left = this.getComparisonData();
628 byte[] right = other.getComparisonData();
629 return Bytes.compareTo(left, right);
630 }
631 }
632
633
634
635
636
637
638 @InterfaceAudience.Private
639 private static class RangeIteration {
640 private boolean exclusive = false;
641 private boolean initialized = false;
642 private boolean foundFirstRange = false;
643 private boolean reversed = false;
644 private final List<RowRange> sortedAndMergedRanges;
645 private List<? extends BasicRowRange> ranges;
646
647 public RangeIteration(List<RowRange> sortedAndMergedRanges) {
648 this.sortedAndMergedRanges = sortedAndMergedRanges;
649 }
650
651 void initialize(boolean reversed) {
652
653 assert !this.initialized;
654 this.reversed = reversed;
655 if (reversed) {
656
657
658 this.ranges = flipAndReverseRanges(sortedAndMergedRanges);
659 } else {
660 this.ranges = sortedAndMergedRanges;
661 }
662 this.initialized = true;
663 }
664
665
666
667
668
669
670 static List<ReversedRowRange> flipAndReverseRanges(List<RowRange> ranges) {
671 List<ReversedRowRange> flippedRanges = new ArrayList<>(ranges.size());
672 for (int i = ranges.size() - 1; i >= 0; i--) {
673 RowRange origRange = ranges.get(i);
674 ReversedRowRange newRowRange = new ReversedRowRange(
675 origRange.startRow, origRange.startRowInclusive, origRange.stopRow,
676 origRange.isStopRowInclusive());
677 flippedRanges.add(newRowRange);
678 }
679 return flippedRanges;
680 }
681
682
683
684
685
686
687
688 @SuppressWarnings("unchecked")
689 public int getNextRangeIndex(byte[] rowKey) {
690
691
692
693 final int index;
694 if (reversed) {
695 ReversedRowRange searchFor = new ReversedRowRange(null, true, rowKey, true);
696 index = Collections.binarySearch((List<ReversedRowRange>) ranges, searchFor);
697 } else {
698 RowRange searchFor = new RowRange(rowKey, true, null, true);
699 index = Collections.binarySearch((List<RowRange>) ranges, searchFor);
700 }
701 if (index < 0) {
702 int insertionPosition = -index - 1;
703
704 if (insertionPosition != 0 && ranges.get(insertionPosition - 1).contains(rowKey)) {
705 return insertionPosition - 1;
706 }
707
708 if (insertionPosition == 0 && !ranges.get(insertionPosition).contains(rowKey)) {
709 return ROW_BEFORE_FIRST_RANGE;
710 }
711 if (!foundFirstRange) {
712 foundFirstRange = true;
713 }
714 return insertionPosition;
715 }
716
717 if(ranges.get(index).isSearchRowInclusive() == false) {
718 exclusive = true;
719 }
720 return index;
721 }
722
723
724
725
726 public void setFoundFirstRange() {
727 this.foundFirstRange = true;
728 }
729
730
731
732
733 @SuppressWarnings("unchecked")
734 public <T extends BasicRowRange> T get(int i) {
735 return (T) ranges.get(i);
736 }
737
738
739
740
741 public boolean hasFoundFirstRange() {
742 return foundFirstRange;
743 }
744
745
746
747
748 public boolean isExclusive() {
749 return exclusive;
750 }
751
752
753
754
755 public void resetExclusive() {
756 exclusive = false;
757 }
758
759
760
761
762 public boolean isInitialized() {
763 return initialized;
764 }
765
766
767
768
769 public boolean isIterationComplete(int index) {
770 return index >= ranges.size();
771 }
772 }
773
774 @Override
775 public boolean equals(Object obj) {
776 return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
777 }
778
779 @Override
780 public int hashCode() {
781 return Objects.hash(this.ranges);
782 }
783 }