1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.client;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.NavigableSet;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.hbase.classification.InterfaceAudience;
35 import org.apache.hadoop.hbase.classification.InterfaceStability;
36 import org.apache.hadoop.hbase.HConstants;
37 import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
38 import org.apache.hadoop.hbase.filter.Filter;
39 import org.apache.hadoop.hbase.filter.IncompatibleFilterException;
40 import org.apache.hadoop.hbase.io.TimeRange;
41 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
42 import org.apache.hadoop.hbase.security.access.Permission;
43 import org.apache.hadoop.hbase.security.visibility.Authorizations;
44 import org.apache.hadoop.hbase.util.Bytes;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 @InterfaceAudience.Public
90 @InterfaceStability.Stable
91 public class Scan extends Query {
92 private static final Log LOG = LogFactory.getLog(Scan.class);
93
94 private static final String RAW_ATTR = "_raw_";
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 public static final String HINT_LOOKAHEAD = "_look_ahead_";
112
113 private byte [] startRow = HConstants.EMPTY_START_ROW;
114 private byte [] stopRow = HConstants.EMPTY_END_ROW;
115 private int maxVersions = 1;
116 private int batch = -1;
117
118 private int storeLimit = -1;
119 private int storeOffset = 0;
120 private boolean getScan;
121
122
123
124
125
126 @Deprecated
127 static public final String SCAN_ATTRIBUTES_METRICS_ENABLE = "scan.attributes.metrics.enable";
128
129
130
131
132
133 @Deprecated
134 static public final String SCAN_ATTRIBUTES_METRICS_DATA = "scan.attributes.metrics.data";
135
136
137
138
139 static public final String SCAN_ATTRIBUTES_TABLE_NAME = "scan.attributes.table.name";
140
141
142
143
144 private int caching = -1;
145 private long maxResultSize = -1;
146 private boolean cacheBlocks = true;
147 private boolean reversed = false;
148 private TimeRange tr = new TimeRange();
149 private Map<byte [], NavigableSet<byte []>> familyMap =
150 new TreeMap<byte [], NavigableSet<byte []>>(Bytes.BYTES_COMPARATOR);
151 private Boolean loadColumnFamiliesOnDemand = null;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 private boolean small = false;
172
173
174
175
176 public Scan() {}
177
178 public Scan(byte [] startRow, Filter filter) {
179 this(startRow);
180 this.filter = filter;
181 }
182
183
184
185
186
187
188
189
190 public Scan(byte [] startRow) {
191 this.startRow = startRow;
192 }
193
194
195
196
197
198
199 public Scan(byte [] startRow, byte [] stopRow) {
200 this.startRow = startRow;
201 this.stopRow = stopRow;
202
203 this.getScan = isStartRowAndEqualsStopRow();
204 }
205
206
207
208
209
210
211
212 public Scan(Scan scan) throws IOException {
213 startRow = scan.getStartRow();
214 stopRow = scan.getStopRow();
215 maxVersions = scan.getMaxVersions();
216 batch = scan.getBatch();
217 storeLimit = scan.getMaxResultsPerColumnFamily();
218 storeOffset = scan.getRowOffsetPerColumnFamily();
219 caching = scan.getCaching();
220 maxResultSize = scan.getMaxResultSize();
221 cacheBlocks = scan.getCacheBlocks();
222 getScan = scan.isGetScan();
223 filter = scan.getFilter();
224 loadColumnFamiliesOnDemand = scan.getLoadColumnFamiliesOnDemandValue();
225 consistency = scan.getConsistency();
226 reversed = scan.isReversed();
227 small = scan.isSmall();
228 TimeRange ctr = scan.getTimeRange();
229 tr = new TimeRange(ctr.getMin(), ctr.getMax());
230 Map<byte[], NavigableSet<byte[]>> fams = scan.getFamilyMap();
231 for (Map.Entry<byte[],NavigableSet<byte[]>> entry : fams.entrySet()) {
232 byte [] fam = entry.getKey();
233 NavigableSet<byte[]> cols = entry.getValue();
234 if (cols != null && cols.size() > 0) {
235 for (byte[] col : cols) {
236 addColumn(fam, col);
237 }
238 } else {
239 addFamily(fam);
240 }
241 }
242 for (Map.Entry<String, byte[]> attr : scan.getAttributesMap().entrySet()) {
243 setAttribute(attr.getKey(), attr.getValue());
244 }
245 }
246
247
248
249
250
251 public Scan(Get get) {
252 this.startRow = get.getRow();
253 this.stopRow = get.getRow();
254 this.filter = get.getFilter();
255 this.cacheBlocks = get.getCacheBlocks();
256 this.maxVersions = get.getMaxVersions();
257 this.storeLimit = get.getMaxResultsPerColumnFamily();
258 this.storeOffset = get.getRowOffsetPerColumnFamily();
259 this.tr = get.getTimeRange();
260 this.familyMap = get.getFamilyMap();
261 this.getScan = true;
262 this.consistency = get.getConsistency();
263 for (Map.Entry<String, byte[]> attr : get.getAttributesMap().entrySet()) {
264 setAttribute(attr.getKey(), attr.getValue());
265 }
266 }
267
268 public boolean isGetScan() {
269 return this.getScan || isStartRowAndEqualsStopRow();
270 }
271
272 private boolean isStartRowAndEqualsStopRow() {
273 return this.startRow != null && this.startRow.length > 0 &&
274 Bytes.equals(this.startRow, this.stopRow);
275 }
276
277
278
279
280
281
282
283 public Scan addFamily(byte [] family) {
284 familyMap.remove(family);
285 familyMap.put(family, null);
286 return this;
287 }
288
289
290
291
292
293
294
295
296
297 public Scan addColumn(byte [] family, byte [] qualifier) {
298 NavigableSet<byte []> set = familyMap.get(family);
299 if(set == null) {
300 set = new TreeSet<byte []>(Bytes.BYTES_COMPARATOR);
301 }
302 if (qualifier == null) {
303 qualifier = HConstants.EMPTY_BYTE_ARRAY;
304 }
305 set.add(qualifier);
306 familyMap.put(family, set);
307 return this;
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321
322 public Scan setTimeRange(long minStamp, long maxStamp)
323 throws IOException {
324 tr = new TimeRange(minStamp, maxStamp);
325 return this;
326 }
327
328
329
330
331
332
333
334
335
336
337
338 public Scan setTimeStamp(long timestamp)
339 throws IOException {
340 try {
341 tr = new TimeRange(timestamp, timestamp+1);
342 } catch(IOException e) {
343
344 LOG.error("TimeRange failed, likely caused by integer overflow. ", e);
345 throw e;
346 }
347 return this;
348 }
349
350
351
352
353
354
355
356 public Scan setStartRow(byte [] startRow) {
357 this.startRow = startRow;
358 return this;
359 }
360
361
362
363
364
365
366
367
368
369
370 public Scan setStopRow(byte [] stopRow) {
371 this.stopRow = stopRow;
372 return this;
373 }
374
375
376
377
378
379
380
381
382
383
384
385
386 public Scan setRowPrefixFilter(byte[] rowPrefix) {
387 if (rowPrefix == null) {
388 setStartRow(HConstants.EMPTY_START_ROW);
389 setStopRow(HConstants.EMPTY_END_ROW);
390 } else {
391 this.setStartRow(rowPrefix);
392 this.setStopRow(calculateTheClosestNextRowKeyForPrefix(rowPrefix));
393 }
394 return this;
395 }
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414 private byte[] calculateTheClosestNextRowKeyForPrefix(byte[] rowKeyPrefix) {
415
416
417 int offset = rowKeyPrefix.length;
418 while (offset > 0) {
419 if (rowKeyPrefix[offset - 1] != (byte) 0xFF) {
420 break;
421 }
422 offset--;
423 }
424
425 if (offset == 0) {
426
427
428
429 return HConstants.EMPTY_END_ROW;
430 }
431
432
433 byte[] newStopRow = Arrays.copyOfRange(rowKeyPrefix, 0, offset);
434
435 newStopRow[newStopRow.length - 1]++;
436 return newStopRow;
437 }
438
439
440
441
442
443 public Scan setMaxVersions() {
444 this.maxVersions = Integer.MAX_VALUE;
445 return this;
446 }
447
448
449
450
451
452
453 public Scan setMaxVersions(int maxVersions) {
454 this.maxVersions = maxVersions;
455 return this;
456 }
457
458
459
460
461
462 public Scan setBatch(int batch) {
463 if (this.hasFilter() && this.filter.hasFilterRow()) {
464 throw new IncompatibleFilterException(
465 "Cannot set batch on a scan using a filter" +
466 " that returns true for filter.hasFilterRow");
467 }
468 this.batch = batch;
469 return this;
470 }
471
472
473
474
475
476 public Scan setMaxResultsPerColumnFamily(int limit) {
477 this.storeLimit = limit;
478 return this;
479 }
480
481
482
483
484
485 public Scan setRowOffsetPerColumnFamily(int offset) {
486 this.storeOffset = offset;
487 return this;
488 }
489
490
491
492
493
494
495
496
497 public Scan setCaching(int caching) {
498 this.caching = caching;
499 return this;
500 }
501
502
503
504
505 public long getMaxResultSize() {
506 return maxResultSize;
507 }
508
509
510
511
512
513
514
515
516 public Scan setMaxResultSize(long maxResultSize) {
517 this.maxResultSize = maxResultSize;
518 return this;
519 }
520
521 @Override
522 public Scan setFilter(Filter filter) {
523 super.setFilter(filter);
524 return this;
525 }
526
527
528
529
530
531
532 public Scan setFamilyMap(Map<byte [], NavigableSet<byte []>> familyMap) {
533 this.familyMap = familyMap;
534 return this;
535 }
536
537
538
539
540
541 public Map<byte [], NavigableSet<byte []>> getFamilyMap() {
542 return this.familyMap;
543 }
544
545
546
547
548 public int numFamilies() {
549 if(hasFamilies()) {
550 return this.familyMap.size();
551 }
552 return 0;
553 }
554
555
556
557
558 public boolean hasFamilies() {
559 return !this.familyMap.isEmpty();
560 }
561
562
563
564
565 public byte[][] getFamilies() {
566 if(hasFamilies()) {
567 return this.familyMap.keySet().toArray(new byte[0][0]);
568 }
569 return null;
570 }
571
572
573
574
575 public byte [] getStartRow() {
576 return this.startRow;
577 }
578
579
580
581
582 public byte [] getStopRow() {
583 return this.stopRow;
584 }
585
586
587
588
589 public int getMaxVersions() {
590 return this.maxVersions;
591 }
592
593
594
595
596 public int getBatch() {
597 return this.batch;
598 }
599
600
601
602
603 public int getMaxResultsPerColumnFamily() {
604 return this.storeLimit;
605 }
606
607
608
609
610
611
612 public int getRowOffsetPerColumnFamily() {
613 return this.storeOffset;
614 }
615
616
617
618
619 public int getCaching() {
620 return this.caching;
621 }
622
623
624
625
626 public TimeRange getTimeRange() {
627 return this.tr;
628 }
629
630
631
632
633 @Override
634 public Filter getFilter() {
635 return filter;
636 }
637
638
639
640
641 public boolean hasFilter() {
642 return filter != null;
643 }
644
645
646
647
648
649
650
651
652
653
654
655 public Scan setCacheBlocks(boolean cacheBlocks) {
656 this.cacheBlocks = cacheBlocks;
657 return this;
658 }
659
660
661
662
663
664
665 public boolean getCacheBlocks() {
666 return cacheBlocks;
667 }
668
669
670
671
672
673
674
675
676
677 public Scan setReversed(boolean reversed) {
678 this.reversed = reversed;
679 return this;
680 }
681
682
683
684
685
686 public boolean isReversed() {
687 return reversed;
688 }
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706 public Scan setLoadColumnFamiliesOnDemand(boolean value) {
707 this.loadColumnFamiliesOnDemand = value;
708 return this;
709 }
710
711
712
713
714 public Boolean getLoadColumnFamiliesOnDemandValue() {
715 return this.loadColumnFamiliesOnDemand;
716 }
717
718
719
720
721 public boolean doLoadColumnFamiliesOnDemand() {
722 return (this.loadColumnFamiliesOnDemand != null)
723 && this.loadColumnFamiliesOnDemand.booleanValue();
724 }
725
726
727
728
729
730
731
732 @Override
733 public Map<String, Object> getFingerprint() {
734 Map<String, Object> map = new HashMap<String, Object>();
735 List<String> families = new ArrayList<String>();
736 if(this.familyMap.size() == 0) {
737 map.put("families", "ALL");
738 return map;
739 } else {
740 map.put("families", families);
741 }
742 for (Map.Entry<byte [], NavigableSet<byte[]>> entry :
743 this.familyMap.entrySet()) {
744 families.add(Bytes.toStringBinary(entry.getKey()));
745 }
746 return map;
747 }
748
749
750
751
752
753
754
755
756 @Override
757 public Map<String, Object> toMap(int maxCols) {
758
759 Map<String, Object> map = getFingerprint();
760
761 Map<String, List<String>> familyColumns =
762 new HashMap<String, List<String>>();
763 map.put("families", familyColumns);
764
765 map.put("startRow", Bytes.toStringBinary(this.startRow));
766 map.put("stopRow", Bytes.toStringBinary(this.stopRow));
767 map.put("maxVersions", this.maxVersions);
768 map.put("batch", this.batch);
769 map.put("caching", this.caching);
770 map.put("maxResultSize", this.maxResultSize);
771 map.put("cacheBlocks", this.cacheBlocks);
772 map.put("loadColumnFamiliesOnDemand", this.loadColumnFamiliesOnDemand);
773 List<Long> timeRange = new ArrayList<Long>();
774 timeRange.add(this.tr.getMin());
775 timeRange.add(this.tr.getMax());
776 map.put("timeRange", timeRange);
777 int colCount = 0;
778
779 for (Map.Entry<byte [], NavigableSet<byte[]>> entry :
780 this.familyMap.entrySet()) {
781 List<String> columns = new ArrayList<String>();
782 familyColumns.put(Bytes.toStringBinary(entry.getKey()), columns);
783 if(entry.getValue() == null) {
784 colCount++;
785 --maxCols;
786 columns.add("ALL");
787 } else {
788 colCount += entry.getValue().size();
789 if (maxCols <= 0) {
790 continue;
791 }
792 for (byte [] column : entry.getValue()) {
793 if (--maxCols <= 0) {
794 continue;
795 }
796 columns.add(Bytes.toStringBinary(column));
797 }
798 }
799 }
800 map.put("totalColumns", colCount);
801 if (this.filter != null) {
802 map.put("filter", this.filter.toString());
803 }
804
805 if (getId() != null) {
806 map.put("id", getId());
807 }
808 return map;
809 }
810
811
812
813
814
815
816
817
818
819
820
821 public Scan setRaw(boolean raw) {
822 setAttribute(RAW_ATTR, Bytes.toBytes(raw));
823 return this;
824 }
825
826
827
828
829 public boolean isRaw() {
830 byte[] attr = getAttribute(RAW_ATTR);
831 return attr == null ? false : Bytes.toBoolean(attr);
832 }
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856 public Scan setSmall(boolean small) {
857 this.small = small;
858 return this;
859 }
860
861
862
863
864
865 public boolean isSmall() {
866 return small;
867 }
868
869 @Override
870 public Scan setAttribute(String name, byte[] value) {
871 return (Scan) super.setAttribute(name, value);
872 }
873
874 @Override
875 public Scan setId(String id) {
876 return (Scan) super.setId(id);
877 }
878
879 @Override
880 public Scan setAuthorizations(Authorizations authorizations) {
881 return (Scan) super.setAuthorizations(authorizations);
882 }
883
884 @Override
885 public Scan setACL(Map<String, Permission> perms) {
886 return (Scan) super.setACL(perms);
887 }
888
889 @Override
890 public Scan setACL(String user, Permission perms) {
891 return (Scan) super.setACL(user, perms);
892 }
893
894 @Override
895 public Scan setConsistency(Consistency consistency) {
896 return (Scan) super.setConsistency(consistency);
897 }
898
899 @Override
900 public Scan setReplicaId(int Id) {
901 return (Scan) super.setReplicaId(Id);
902 }
903
904 @Override
905 public Scan setIsolationLevel(IsolationLevel level) {
906 return (Scan) super.setIsolationLevel(level);
907 }
908
909
910
911
912
913
914
915
916
917 static Scan createGetClosestRowOrBeforeReverseScan(byte[] row) {
918
919
920 Scan scan = new Scan(row);
921 scan.setSmall(true);
922 scan.setReversed(true);
923 scan.setCaching(1);
924 return scan;
925 }
926
927
928
929
930
931 public Scan setScanMetricsEnabled(final boolean enabled) {
932 setAttribute(Scan.SCAN_ATTRIBUTES_METRICS_ENABLE, Bytes.toBytes(Boolean.valueOf(enabled)));
933 return this;
934 }
935
936
937
938
939 public boolean isScanMetricsEnabled() {
940 byte[] attr = getAttribute(Scan.SCAN_ATTRIBUTES_METRICS_ENABLE);
941 return attr == null ? false : Bytes.toBoolean(attr);
942 }
943
944
945
946
947
948 public ScanMetrics getScanMetrics() {
949 byte [] bytes = getAttribute(Scan.SCAN_ATTRIBUTES_METRICS_DATA);
950 if (bytes == null) return null;
951 return ProtobufUtil.toScanMetrics(bytes);
952 }
953 }