1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.client;
20
21 import java.io.IOException;
22 import java.util.Arrays;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.apache.hadoop.classification.InterfaceAudience;
27 import org.apache.hadoop.classification.InterfaceStability;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.HConstants;
30 import org.apache.hadoop.hbase.TableName;
31 import org.apache.hadoop.hbase.util.Bytes;
32 import org.apache.hadoop.hbase.util.ExceptionUtil;
33
34
35
36
37 @InterfaceAudience.Public
38 @InterfaceStability.Evolving
39 public class ReversedClientScanner extends ClientScanner {
40 private static final Log LOG = LogFactory.getLog(ReversedClientScanner.class);
41
42
43 static byte[] MAX_BYTE_ARRAY = Bytes.createMaxByteArray(9);
44
45
46
47
48
49
50
51
52
53 public ReversedClientScanner(Configuration conf, Scan scan,
54 TableName tableName, HConnection connection) throws IOException {
55 super(conf, scan, tableName, connection);
56 }
57
58 @Override
59 protected boolean nextScanner(int nbRows, final boolean done)
60 throws IOException {
61
62 if (this.callable != null) {
63 this.callable.setClose();
64 this.caller.callWithRetries(callable);
65 this.callable = null;
66 }
67
68
69 byte[] localStartKey;
70 boolean locateTheClosestFrontRow = true;
71
72 if (this.currentRegion != null) {
73 byte[] startKey = this.currentRegion.getStartKey();
74 if (startKey == null
75 || Bytes.equals(startKey, HConstants.EMPTY_BYTE_ARRAY)
76 || checkScanStopRow(startKey) || done) {
77 close();
78 if (LOG.isDebugEnabled()) {
79 LOG.debug("Finished " + this.currentRegion);
80 }
81 return false;
82 }
83 localStartKey = startKey;
84 if (LOG.isDebugEnabled()) {
85 LOG.debug("Finished " + this.currentRegion);
86 }
87 } else {
88 localStartKey = this.scan.getStartRow();
89 if (!Bytes.equals(localStartKey, HConstants.EMPTY_BYTE_ARRAY)) {
90 locateTheClosestFrontRow = false;
91 }
92 }
93
94 if (LOG.isDebugEnabled() && this.currentRegion != null) {
95
96 LOG.debug("Advancing internal scanner to startKey at '"
97 + Bytes.toStringBinary(localStartKey) + "'");
98 }
99 try {
100
101
102
103
104
105
106
107 byte[] locateStartRow = locateTheClosestFrontRow ? createClosestRowBefore(localStartKey)
108 : null;
109 callable = getScannerCallable(localStartKey, nbRows, locateStartRow);
110
111
112 this.caller.callWithRetries(callable);
113 this.currentRegion = callable.getHRegionInfo();
114 if (this.scanMetrics != null) {
115 this.scanMetrics.countOfRegions.incrementAndGet();
116 }
117 } catch (IOException e) {
118 ExceptionUtil.rethrowIfInterrupt(e);
119 close();
120 throw e;
121 }
122 return true;
123 }
124
125 protected ScannerCallable getScannerCallable(byte[] localStartKey,
126 int nbRows, byte[] locateStartRow) {
127 scan.setStartRow(localStartKey);
128 ScannerCallable s = new ReversedScannerCallable(getConnection(),
129 getTable(), scan, this.scanMetrics, locateStartRow);
130 s.setCaching(nbRows);
131 return s;
132 }
133
134 @Override
135
136 protected boolean checkScanStopRow(final byte[] startKey) {
137 if (this.scan.getStopRow().length > 0) {
138
139 byte[] stopRow = scan.getStopRow();
140 int cmp = Bytes.compareTo(stopRow, 0, stopRow.length, startKey, 0,
141 startKey.length);
142 if (cmp >= 0) {
143
144
145 return true;
146 }
147 }
148 return false;
149 }
150
151
152
153
154
155
156 protected byte[] createClosestRowBefore(byte[] row) {
157 if (row == null) {
158 throw new IllegalArgumentException("The passed row is empty");
159 }
160 if (Bytes.equals(row, HConstants.EMPTY_BYTE_ARRAY)) {
161 return MAX_BYTE_ARRAY;
162 }
163 if (row[row.length - 1] == 0) {
164 return Arrays.copyOf(row, row.length - 1);
165 } else {
166 byte[] closestFrontRow = Arrays.copyOf(row, row.length);
167 closestFrontRow[row.length - 1] = (byte) ((closestFrontRow[row.length - 1] & 0xff) - 1);
168 closestFrontRow = Bytes.add(closestFrontRow, MAX_BYTE_ARRAY);
169 return closestFrontRow;
170 }
171 }
172
173 }