1 /*
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 package org.apache.hadoop.hbase.client;
21
22 import java.io.IOException;
23 import java.nio.BufferOverflowException;
24 import java.nio.ByteBuffer;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Comparator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.NavigableMap;
31 import java.util.NoSuchElementException;
32 import java.util.TreeMap;
33
34 import org.apache.hadoop.hbase.Cell;
35 import org.apache.hadoop.hbase.CellScannable;
36 import org.apache.hadoop.hbase.CellScanner;
37 import org.apache.hadoop.hbase.CellUtil;
38 import org.apache.hadoop.hbase.HConstants;
39 import org.apache.hadoop.hbase.KeyValue;
40 import org.apache.hadoop.hbase.KeyValueUtil;
41 import org.apache.hadoop.hbase.classification.InterfaceAudience;
42 import org.apache.hadoop.hbase.classification.InterfaceStability;
43 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
44 import org.apache.hadoop.hbase.util.Bytes;
45
46 /**
47 * Single row result of a {@link Get} or {@link Scan} query.<p>
48 *
49 * This class is <b>NOT THREAD SAFE</b>.<p>
50 *
51 * Convenience methods are available that return various {@link Map}
52 * structures and values directly.<p>
53 *
54 * To get a complete mapping of all cells in the Result, which can include
55 * multiple families and multiple versions, use {@link #getMap()}.<p>
56 *
57 * To get a mapping of each family to its columns (qualifiers and values),
58 * including only the latest version of each, use {@link #getNoVersionMap()}.
59 *
60 * To get a mapping of qualifiers to latest values for an individual family use
61 * {@link #getFamilyMap(byte[])}.<p>
62 *
63 * To get the latest value for a specific family and qualifier use {@link #getValue(byte[], byte[])}.
64 *
65 * A Result is backed by an array of {@link Cell} objects, each representing
66 * an HBase cell defined by the row, family, qualifier, timestamp, and value.<p>
67 *
68 * The underlying {@link Cell} objects can be accessed through the method {@link #listCells()}.
69 * This will create a List from the internal Cell []. Better is to exploit the fact that
70 * a new Result instance is a primed {@link CellScanner}; just call {@link #advance()} and
71 * {@link #current()} to iterate over Cells as you would any {@link CellScanner}.
72 * Call {@link #cellScanner()} to reset should you need to iterate the same Result over again
73 * ({@link CellScanner}s are one-shot).
74 *
75 * If you need to overwrite a Result with another Result instance -- as in the old 'mapred'
76 * RecordReader next invocations -- then create an empty Result with the null constructor and
77 * in then use {@link #copyFrom(Result)}
78 */
79 @InterfaceAudience.Public
80 @InterfaceStability.Stable
81 public class Result implements CellScannable, CellScanner {
82 private Cell[] cells;
83 private Boolean exists; // if the query was just to check existence.
84 private boolean stale = false;
85
86 /**
87 * Partial results do not contain the full row's worth of cells. The result had to be returned in
88 * parts because the size of the cells in the row exceeded the RPC result size on the server.
89 * Partial results must be combined client side with results representing the remainder of the
90 * row's cells to form the complete result. Partial results and RPC result size allow us to avoid
91 * OOME on the server when servicing requests for large rows. The Scan configuration used to
92 * control the result size on the server is {@link Scan#setMaxResultSize(long)} and the default
93 * value can be seen here: {@link HConstants#DEFAULT_HBASE_CLIENT_SCANNER_MAX_RESULT_SIZE}
94 */
95 private boolean partial = false;
96 // We're not using java serialization. Transient here is just a marker to say
97 // that this is where we cache row if we're ever asked for it.
98 private transient byte [] row = null;
99 // Ditto for familyMap. It can be composed on fly from passed in kvs.
100 private transient NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyMap = null;
101
102 private static ThreadLocal<byte[]> localBuffer = new ThreadLocal<byte[]>();
103 private static final int PAD_WIDTH = 128;
104 public static final Result EMPTY_RESULT = new Result(true);
105
106 private final static int INITIAL_CELLSCANNER_INDEX = -1;
107
108 /**
109 * Index for where we are when Result is acting as a {@link CellScanner}.
110 */
111 private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
112 private ClientProtos.RegionLoadStats stats;
113
114 private final boolean readonly;
115
116 /**
117 * Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
118 * Use this to represent no results if <code>null</code> won't do or in old 'mapred' as oppposed to 'mapreduce' package
119 * MapReduce where you need to overwrite a Result
120 * instance with a {@link #copyFrom(Result)} call.
121 */
122 public Result() {
123 this(false);
124 }
125
126 /**
127 * Allows to construct special purpose immutable Result objects,
128 * such as EMPTY_RESULT.
129 * @param readonly whether this Result instance is readonly
130 */
131 private Result(boolean readonly) {
132 this.readonly = readonly;
133 }
134
135 /**
136 * @deprecated Use {@link #create(List)} instead.
137 */
138 @Deprecated
139 public Result(KeyValue [] cells) {
140 this(cells, null, false, false);
141 }
142
143 /**
144 * @deprecated Use {@link #create(List)} instead.
145 */
146 @Deprecated
147 public Result(List<KeyValue> kvs) {
148 // TODO: Here we presume the passed in Cells are KVs. One day this won't always be so.
149 this(kvs.toArray(new Cell[kvs.size()]), null, false, false);
150 }
151
152 /**
153 * Instantiate a Result with the specified List of KeyValues.
154 * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
155 * @param cells List of cells
156 */
157 public static Result create(List<Cell> cells) {
158 return create(cells, null);
159 }
160
161 public static Result create(List<Cell> cells, Boolean exists) {
162 return create(cells, exists, false);
163 }
164
165 public static Result create(List<Cell> cells, Boolean exists, boolean stale) {
166 return create(cells, exists, stale, false);
167 }
168
169 public static Result create(List<Cell> cells, Boolean exists, boolean stale, boolean partial) {
170 if (exists != null){
171 return new Result(null, exists, stale, partial);
172 }
173 return new Result(cells.toArray(new Cell[cells.size()]), null, stale, partial);
174 }
175
176 /**
177 * Instantiate a Result with the specified array of KeyValues.
178 * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
179 * @param cells array of cells
180 */
181 public static Result create(Cell[] cells) {
182 return create(cells, null, false);
183 }
184
185 public static Result create(Cell[] cells, Boolean exists, boolean stale) {
186 return create(cells, exists, stale, false);
187 }
188
189 public static Result create(Cell[] cells, Boolean exists, boolean stale, boolean partial) {
190 if (exists != null){
191 return new Result(null, exists, stale, partial);
192 }
193 return new Result(cells, null, stale, partial);
194 }
195
196 /** Private ctor. Use {@link #create(Cell[])}. */
197 private Result(Cell[] cells, Boolean exists, boolean stale, boolean partial) {
198 this.cells = cells;
199 this.exists = exists;
200 this.stale = stale;
201 this.partial = partial;
202 this.readonly = false;
203 }
204
205 /**
206 * Method for retrieving the row key that corresponds to
207 * the row from which this Result was created.
208 * @return row
209 */
210 public byte [] getRow() {
211 if (this.row == null) {
212 this.row = this.cells == null || this.cells.length == 0? null: CellUtil.cloneRow(this.cells[0]);
213 }
214 return this.row;
215 }
216
217 /**
218 * Return the array of Cells backing this Result instance.
219 *
220 * The array is sorted from smallest -> largest using the
221 * {@link KeyValue#COMPARATOR}.
222 *
223 * The array only contains what your Get or Scan specifies and no more.
224 * For example if you request column "A" 1 version you will have at most 1
225 * Cell in the array. If you request column "A" with 2 version you will
226 * have at most 2 Cells, with the first one being the newer timestamp and
227 * the second being the older timestamp (this is the sort order defined by
228 * {@link KeyValue#COMPARATOR}). If columns don't exist, they won't be
229 * present in the result. Therefore if you ask for 1 version all columns,
230 * it is safe to iterate over this array and expect to see 1 Cell for
231 * each column and no more.
232 *
233 * This API is faster than using getFamilyMap() and getMap()
234 *
235 * @return array of Cells; can be null if nothing in the result
236 */
237 public Cell[] rawCells() {
238 return cells;
239 }
240
241 /**
242 * Return an cells of a Result as an array of KeyValues
243 *
244 * WARNING do not use, expensive. This does an arraycopy of the cell[]'s value.
245 *
246 * Added to ease transition from 0.94 -> 0.96.
247 *
248 * @deprecated as of 0.96, use {@link #rawCells()}
249 * @return array of KeyValues, empty array if nothing in result.
250 */
251 @Deprecated
252 public KeyValue[] raw() {
253 KeyValue[] kvs = new KeyValue[cells.length];
254 for (int i = 0 ; i < kvs.length; i++) {
255 kvs[i] = KeyValueUtil.ensureKeyValue(cells[i]);
256 }
257 return kvs;
258 }
259
260 /**
261 * Create a sorted list of the Cell's in this result.
262 *
263 * Since HBase 0.20.5 this is equivalent to raw().
264 *
265 * @return sorted List of Cells; can be null if no cells in the result
266 */
267 public List<Cell> listCells() {
268 return isEmpty()? null: Arrays.asList(rawCells());
269 }
270
271 /**
272 * Return an cells of a Result as an array of KeyValues
273 *
274 * WARNING do not use, expensive. This does an arraycopy of the cell[]'s value.
275 *
276 * Added to ease transition from 0.94 -> 0.96.
277 *
278 * @deprecated as of 0.96, use {@link #listCells()}
279 * @return all sorted List of KeyValues; can be null if no cells in the result
280 */
281 @Deprecated
282 public List<KeyValue> list() {
283 return isEmpty() ? null : Arrays.asList(raw());
284 }
285
286 /**
287 * @deprecated Use {@link #getColumnCells(byte[], byte[])} instead.
288 */
289 @Deprecated
290 public List<KeyValue> getColumn(byte [] family, byte [] qualifier) {
291 return KeyValueUtil.ensureKeyValues(getColumnCells(family, qualifier));
292 }
293
294 /**
295 * Return the Cells for the specific column. The Cells are sorted in
296 * the {@link KeyValue#COMPARATOR} order. That implies the first entry in
297 * the list is the most recent column. If the query (Scan or Get) only
298 * requested 1 version the list will contain at most 1 entry. If the column
299 * did not exist in the result set (either the column does not exist
300 * or the column was not selected in the query) the list will be empty.
301 *
302 * Also see getColumnLatest which returns just a Cell
303 *
304 * @param family the family
305 * @param qualifier
306 * @return a list of Cells for this column or empty list if the column
307 * did not exist in the result set
308 */
309 public List<Cell> getColumnCells(byte [] family, byte [] qualifier) {
310 List<Cell> result = new ArrayList<Cell>();
311
312 Cell [] kvs = rawCells();
313
314 if (kvs == null || kvs.length == 0) {
315 return result;
316 }
317 int pos = binarySearch(kvs, family, qualifier);
318 if (pos == -1) {
319 return result; // cant find it
320 }
321
322 for (int i = pos ; i < kvs.length ; i++ ) {
323 if (CellUtil.matchingColumn(kvs[i], family,qualifier)) {
324 result.add(kvs[i]);
325 } else {
326 break;
327 }
328 }
329
330 return result;
331 }
332
333 protected int binarySearch(final Cell [] kvs,
334 final byte [] family,
335 final byte [] qualifier) {
336 Cell searchTerm =
337 KeyValueUtil.createFirstOnRow(CellUtil.cloneRow(kvs[0]),
338 family, qualifier);
339
340 // pos === ( -(insertion point) - 1)
341 int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
342 // never will exact match
343 if (pos < 0) {
344 pos = (pos+1) * -1;
345 // pos is now insertion point
346 }
347 if (pos == kvs.length) {
348 return -1; // doesn't exist
349 }
350 return pos;
351 }
352
353 /**
354 * Searches for the latest value for the specified column.
355 *
356 * @param kvs the array to search
357 * @param family family name
358 * @param foffset family offset
359 * @param flength family length
360 * @param qualifier column qualifier
361 * @param qoffset qualifier offset
362 * @param qlength qualifier length
363 *
364 * @return the index where the value was found, or -1 otherwise
365 */
366 protected int binarySearch(final Cell [] kvs,
367 final byte [] family, final int foffset, final int flength,
368 final byte [] qualifier, final int qoffset, final int qlength) {
369
370 double keyValueSize = (double)
371 KeyValue.getKeyValueDataStructureSize(kvs[0].getRowLength(), flength, qlength, 0);
372
373 byte[] buffer = localBuffer.get();
374 if (buffer == null || keyValueSize > buffer.length) {
375 // pad to the smallest multiple of the pad width
376 buffer = new byte[(int) Math.ceil(keyValueSize / PAD_WIDTH) * PAD_WIDTH];
377 localBuffer.set(buffer);
378 }
379
380 Cell searchTerm = KeyValueUtil.createFirstOnRow(buffer, 0,
381 kvs[0].getRowArray(), kvs[0].getRowOffset(), kvs[0].getRowLength(),
382 family, foffset, flength,
383 qualifier, qoffset, qlength);
384
385 // pos === ( -(insertion point) - 1)
386 int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
387 // never will exact match
388 if (pos < 0) {
389 pos = (pos+1) * -1;
390 // pos is now insertion point
391 }
392 if (pos == kvs.length) {
393 return -1; // doesn't exist
394 }
395 return pos;
396 }
397
398 /**
399 * @deprecated Use {@link #getColumnLatestCell(byte[], byte[])} instead.
400 */
401 @Deprecated
402 public KeyValue getColumnLatest(byte [] family, byte [] qualifier) {
403 return KeyValueUtil.ensureKeyValue(getColumnLatestCell(family, qualifier));
404 }
405
406 /**
407 * The Cell for the most recent timestamp for a given column.
408 *
409 * @param family
410 * @param qualifier
411 *
412 * @return the Cell for the column, or null if no value exists in the row or none have been
413 * selected in the query (Get/Scan)
414 */
415 public Cell getColumnLatestCell(byte [] family, byte [] qualifier) {
416 Cell [] kvs = rawCells(); // side effect possibly.
417 if (kvs == null || kvs.length == 0) {
418 return null;
419 }
420 int pos = binarySearch(kvs, family, qualifier);
421 if (pos == -1) {
422 return null;
423 }
424 if (CellUtil.matchingColumn(kvs[pos], family, qualifier)) {
425 return kvs[pos];
426 }
427 return null;
428 }
429
430 /**
431 * @deprecated Use {@link #getColumnLatestCell(byte[], int, int, byte[], int, int)} instead.
432 */
433 @Deprecated
434 public KeyValue getColumnLatest(byte [] family, int foffset, int flength,
435 byte [] qualifier, int qoffset, int qlength) {
436 return KeyValueUtil.ensureKeyValue(
437 getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength));
438 }
439
440 /**
441 * The Cell for the most recent timestamp for a given column.
442 *
443 * @param family family name
444 * @param foffset family offset
445 * @param flength family length
446 * @param qualifier column qualifier
447 * @param qoffset qualifier offset
448 * @param qlength qualifier length
449 *
450 * @return the Cell for the column, or null if no value exists in the row or none have been
451 * selected in the query (Get/Scan)
452 */
453 public Cell getColumnLatestCell(byte [] family, int foffset, int flength,
454 byte [] qualifier, int qoffset, int qlength) {
455
456 Cell [] kvs = rawCells(); // side effect possibly.
457 if (kvs == null || kvs.length == 0) {
458 return null;
459 }
460 int pos = binarySearch(kvs, family, foffset, flength, qualifier, qoffset, qlength);
461 if (pos == -1) {
462 return null;
463 }
464 if (CellUtil.matchingColumn(kvs[pos], family, foffset, flength, qualifier, qoffset, qlength)) {
465 return kvs[pos];
466 }
467 return null;
468 }
469
470 /**
471 * Get the latest version of the specified column.
472 * Note: this call clones the value content of the hosting Cell. See
473 * {@link #getValueAsByteBuffer(byte[], byte[])}, etc., or {@link #listCells()} if you would
474 * avoid the cloning.
475 * @param family family name
476 * @param qualifier column qualifier
477 * @return value of latest version of column, null if none found
478 */
479 public byte[] getValue(byte [] family, byte [] qualifier) {
480 Cell kv = getColumnLatestCell(family, qualifier);
481 if (kv == null) {
482 return null;
483 }
484 return CellUtil.cloneValue(kv);
485 }
486
487 /**
488 * Returns the value wrapped in a new <code>ByteBuffer</code>.
489 *
490 * @param family family name
491 * @param qualifier column qualifier
492 *
493 * @return the latest version of the column, or <code>null</code> if none found
494 */
495 public ByteBuffer getValueAsByteBuffer(byte [] family, byte [] qualifier) {
496
497 Cell kv = getColumnLatestCell(family, 0, family.length, qualifier, 0, qualifier.length);
498
499 if (kv == null) {
500 return null;
501 }
502 return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()).
503 asReadOnlyBuffer();
504 }
505
506 /**
507 * Returns the value wrapped in a new <code>ByteBuffer</code>.
508 *
509 * @param family family name
510 * @param foffset family offset
511 * @param flength family length
512 * @param qualifier column qualifier
513 * @param qoffset qualifier offset
514 * @param qlength qualifier length
515 *
516 * @return the latest version of the column, or <code>null</code> if none found
517 */
518 public ByteBuffer getValueAsByteBuffer(byte [] family, int foffset, int flength,
519 byte [] qualifier, int qoffset, int qlength) {
520
521 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
522
523 if (kv == null) {
524 return null;
525 }
526 return ByteBuffer.wrap(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength()).
527 asReadOnlyBuffer();
528 }
529
530 /**
531 * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
532 * <p>
533 * Does not clear or flip the buffer.
534 *
535 * @param family family name
536 * @param qualifier column qualifier
537 * @param dst the buffer where to write the value
538 *
539 * @return <code>true</code> if a value was found, <code>false</code> otherwise
540 *
541 * @throws BufferOverflowException there is insufficient space remaining in the buffer
542 */
543 public boolean loadValue(byte [] family, byte [] qualifier, ByteBuffer dst)
544 throws BufferOverflowException {
545 return loadValue(family, 0, family.length, qualifier, 0, qualifier.length, dst);
546 }
547
548 /**
549 * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
550 * <p>
551 * Does not clear or flip the buffer.
552 *
553 * @param family family name
554 * @param foffset family offset
555 * @param flength family length
556 * @param qualifier column qualifier
557 * @param qoffset qualifier offset
558 * @param qlength qualifier length
559 * @param dst the buffer where to write the value
560 *
561 * @return <code>true</code> if a value was found, <code>false</code> otherwise
562 *
563 * @throws BufferOverflowException there is insufficient space remaining in the buffer
564 */
565 public boolean loadValue(byte [] family, int foffset, int flength,
566 byte [] qualifier, int qoffset, int qlength, ByteBuffer dst)
567 throws BufferOverflowException {
568 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
569
570 if (kv == null) {
571 return false;
572 }
573 dst.put(kv.getValueArray(), kv.getValueOffset(), kv.getValueLength());
574 return true;
575 }
576
577 /**
578 * Checks if the specified column contains a non-empty value (not a zero-length byte array).
579 *
580 * @param family family name
581 * @param qualifier column qualifier
582 *
583 * @return whether or not a latest value exists and is not empty
584 */
585 public boolean containsNonEmptyColumn(byte [] family, byte [] qualifier) {
586
587 return containsNonEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
588 }
589
590 /**
591 * Checks if the specified column contains a non-empty value (not a zero-length byte array).
592 *
593 * @param family family name
594 * @param foffset family offset
595 * @param flength family length
596 * @param qualifier column qualifier
597 * @param qoffset qualifier offset
598 * @param qlength qualifier length
599 *
600 * @return whether or not a latest value exists and is not empty
601 */
602 public boolean containsNonEmptyColumn(byte [] family, int foffset, int flength,
603 byte [] qualifier, int qoffset, int qlength) {
604
605 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
606
607 return (kv != null) && (kv.getValueLength() > 0);
608 }
609
610 /**
611 * Checks if the specified column contains an empty value (a zero-length byte array).
612 *
613 * @param family family name
614 * @param qualifier column qualifier
615 *
616 * @return whether or not a latest value exists and is empty
617 */
618 public boolean containsEmptyColumn(byte [] family, byte [] qualifier) {
619
620 return containsEmptyColumn(family, 0, family.length, qualifier, 0, qualifier.length);
621 }
622
623 /**
624 * Checks if the specified column contains an empty value (a zero-length byte array).
625 *
626 * @param family family name
627 * @param foffset family offset
628 * @param flength family length
629 * @param qualifier column qualifier
630 * @param qoffset qualifier offset
631 * @param qlength qualifier length
632 *
633 * @return whether or not a latest value exists and is empty
634 */
635 public boolean containsEmptyColumn(byte [] family, int foffset, int flength,
636 byte [] qualifier, int qoffset, int qlength) {
637 Cell kv = getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength);
638
639 return (kv != null) && (kv.getValueLength() == 0);
640 }
641
642 /**
643 * Checks for existence of a value for the specified column (empty or not).
644 *
645 * @param family family name
646 * @param qualifier column qualifier
647 *
648 * @return true if at least one value exists in the result, false if not
649 */
650 public boolean containsColumn(byte [] family, byte [] qualifier) {
651 Cell kv = getColumnLatestCell(family, qualifier);
652 return kv != null;
653 }
654
655 /**
656 * Checks for existence of a value for the specified column (empty or not).
657 *
658 * @param family family name
659 * @param foffset family offset
660 * @param flength family length
661 * @param qualifier column qualifier
662 * @param qoffset qualifier offset
663 * @param qlength qualifier length
664 *
665 * @return true if at least one value exists in the result, false if not
666 */
667 public boolean containsColumn(byte [] family, int foffset, int flength,
668 byte [] qualifier, int qoffset, int qlength) {
669
670 return getColumnLatestCell(family, foffset, flength, qualifier, qoffset, qlength) != null;
671 }
672
673 /**
674 * Map of families to all versions of its qualifiers and values.
675 * <p>
676 * Returns a three level Map of the form:
677 * <code>Map&family,Map<qualifier,Map<timestamp,value>>></code>
678 * <p>
679 * Note: All other map returning methods make use of this map internally.
680 * @return map from families to qualifiers to versions
681 */
682 public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
683 if (this.familyMap != null) {
684 return this.familyMap;
685 }
686 if(isEmpty()) {
687 return null;
688 }
689 this.familyMap = new TreeMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>(Bytes.BYTES_COMPARATOR);
690 for(Cell kv : this.cells) {
691 byte [] family = CellUtil.cloneFamily(kv);
692 NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap =
693 familyMap.get(family);
694 if(columnMap == null) {
695 columnMap = new TreeMap<byte[], NavigableMap<Long, byte[]>>
696 (Bytes.BYTES_COMPARATOR);
697 familyMap.put(family, columnMap);
698 }
699 byte [] qualifier = CellUtil.cloneQualifier(kv);
700 NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier);
701 if(versionMap == null) {
702 versionMap = new TreeMap<Long, byte[]>(new Comparator<Long>() {
703 @Override
704 public int compare(Long l1, Long l2) {
705 return l2.compareTo(l1);
706 }
707 });
708 columnMap.put(qualifier, versionMap);
709 }
710 Long timestamp = kv.getTimestamp();
711 byte [] value = CellUtil.cloneValue(kv);
712
713 versionMap.put(timestamp, value);
714 }
715 return this.familyMap;
716 }
717
718 /**
719 * Map of families to their most recent qualifiers and values.
720 * <p>
721 * Returns a two level Map of the form: <code>Map&family,Map<qualifier,value>></code>
722 * <p>
723 * The most recent version of each qualifier will be used.
724 * @return map from families to qualifiers and value
725 */
726 public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
727 if(this.familyMap == null) {
728 getMap();
729 }
730 if(isEmpty()) {
731 return null;
732 }
733 NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap =
734 new TreeMap<byte[], NavigableMap<byte[], byte[]>>(Bytes.BYTES_COMPARATOR);
735 for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
736 familyEntry : familyMap.entrySet()) {
737 NavigableMap<byte[], byte[]> qualifierMap =
738 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
739 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry :
740 familyEntry.getValue().entrySet()) {
741 byte [] value =
742 qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey());
743 qualifierMap.put(qualifierEntry.getKey(), value);
744 }
745 returnMap.put(familyEntry.getKey(), qualifierMap);
746 }
747 return returnMap;
748 }
749
750 /**
751 * Map of qualifiers to values.
752 * <p>
753 * Returns a Map of the form: <code>Map<qualifier,value></code>
754 * @param family column family to get
755 * @return map of qualifiers to values
756 */
757 public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
758 if(this.familyMap == null) {
759 getMap();
760 }
761 if(isEmpty()) {
762 return null;
763 }
764 NavigableMap<byte[], byte[]> returnMap =
765 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
766 NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap =
767 familyMap.get(family);
768 if(qualifierMap == null) {
769 return returnMap;
770 }
771 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> entry :
772 qualifierMap.entrySet()) {
773 byte [] value =
774 entry.getValue().get(entry.getValue().firstKey());
775 returnMap.put(entry.getKey(), value);
776 }
777 return returnMap;
778 }
779
780 /**
781 * Returns the value of the first column in the Result.
782 * @return value of the first column
783 */
784 public byte [] value() {
785 if (isEmpty()) {
786 return null;
787 }
788 return CellUtil.cloneValue(cells[0]);
789 }
790
791 /**
792 * Check if the underlying Cell [] is empty or not
793 * @return true if empty
794 */
795 public boolean isEmpty() {
796 return this.cells == null || this.cells.length == 0;
797 }
798
799 /**
800 * @return the size of the underlying Cell []
801 */
802 public int size() {
803 return this.cells == null? 0: this.cells.length;
804 }
805
806 /**
807 * @return String
808 */
809 @Override
810 public String toString() {
811 StringBuilder sb = new StringBuilder();
812 sb.append("keyvalues=");
813 if(isEmpty()) {
814 sb.append("NONE");
815 return sb.toString();
816 }
817 sb.append("{");
818 boolean moreThanOne = false;
819 for(Cell kv : this.cells) {
820 if(moreThanOne) {
821 sb.append(", ");
822 } else {
823 moreThanOne = true;
824 }
825 sb.append(kv.toString());
826 }
827 sb.append("}");
828 return sb.toString();
829 }
830
831 /**
832 * Does a deep comparison of two Results, down to the byte arrays.
833 * @param res1 first result to compare
834 * @param res2 second result to compare
835 * @throws Exception Every difference is throwing an exception
836 */
837 public static void compareResults(Result res1, Result res2)
838 throws Exception {
839 if (res2 == null) {
840 throw new Exception("There wasn't enough rows, we stopped at "
841 + Bytes.toStringBinary(res1.getRow()));
842 }
843 if (res1.size() != res2.size()) {
844 throw new Exception("This row doesn't have the same number of KVs: "
845 + res1.toString() + " compared to " + res2.toString());
846 }
847 Cell[] ourKVs = res1.rawCells();
848 Cell[] replicatedKVs = res2.rawCells();
849 for (int i = 0; i < res1.size(); i++) {
850 if (!ourKVs[i].equals(replicatedKVs[i]) ||
851 !Bytes.equals(CellUtil.cloneValue(ourKVs[i]), CellUtil.cloneValue(replicatedKVs[i]))) {
852 throw new Exception("This result was different: "
853 + res1.toString() + " compared to " + res2.toString());
854 }
855 }
856 }
857
858 /**
859 * Forms a single result from the partial results in the partialResults list. This method is
860 * useful for reconstructing partial results on the client side.
861 * @param partialResults list of partial results
862 * @return The complete result that is formed by combining all of the partial results together
863 * @throws IOException A complete result cannot be formed because the results in the partial list
864 * come from different rows
865 */
866 public static Result createCompleteResult(List<Result> partialResults)
867 throws IOException {
868 List<Cell> cells = new ArrayList<Cell>();
869 boolean stale = false;
870 byte[] prevRow = null;
871 byte[] currentRow = null;
872
873 if (partialResults != null && !partialResults.isEmpty()) {
874 for (int i = 0; i < partialResults.size(); i++) {
875 Result r = partialResults.get(i);
876 currentRow = r.getRow();
877 if (prevRow != null && !Bytes.equals(prevRow, currentRow)) {
878 throw new IOException(
879 "Cannot form complete result. Rows of partial results do not match." +
880 " Partial Results: " + partialResults);
881 }
882
883 // Ensure that all Results except the last one are marked as partials. The last result
884 // may not be marked as a partial because Results are only marked as partials when
885 // the scan on the server side must be stopped due to reaching the maxResultSize.
886 // Visualizing it makes it easier to understand:
887 // maxResultSize: 2 cells
888 // (-x-) represents cell number x in a row
889 // Example: row1: -1- -2- -3- -4- -5- (5 cells total)
890 // How row1 will be returned by the server as partial Results:
891 // Result1: -1- -2- (2 cells, size limit reached, mark as partial)
892 // Result2: -3- -4- (2 cells, size limit reached, mark as partial)
893 // Result3: -5- (1 cell, size limit NOT reached, NOT marked as partial)
894 if (i != (partialResults.size() - 1) && !r.isPartial()) {
895 throw new IOException(
896 "Cannot form complete result. Result is missing partial flag. " +
897 "Partial Results: " + partialResults);
898 }
899 prevRow = currentRow;
900 stale = stale || r.isStale();
901 for (Cell c : r.rawCells()) {
902 cells.add(c);
903 }
904 }
905 }
906
907 return Result.create(cells, null, stale);
908 }
909
910 /**
911 * Get total size of raw cells
912 * @param result
913 * @return Total size.
914 */
915 public static long getTotalSizeOfCells(Result result) {
916 long size = 0;
917 if (result.isEmpty()) {
918 return size;
919 }
920 for (Cell c : result.rawCells()) {
921 size += CellUtil.estimatedHeapSizeOf(c);
922 }
923 return size;
924 }
925
926 /**
927 * Copy another Result into this one. Needed for the old Mapred framework
928 * @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
929 * (which is supposed to be immutable).
930 * @param other
931 */
932 public void copyFrom(Result other) {
933 checkReadonly();
934 this.row = null;
935 this.familyMap = null;
936 this.cells = other.cells;
937 }
938
939 @Override
940 public CellScanner cellScanner() {
941 // Reset
942 this.cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
943 return this;
944 }
945
946 @Override
947 public Cell current() {
948 if (cells == null
949 || cellScannerIndex == INITIAL_CELLSCANNER_INDEX
950 || cellScannerIndex >= cells.length)
951 return null;
952 return this.cells[cellScannerIndex];
953 }
954
955 @Override
956 public boolean advance() {
957 if (cells == null) return false;
958 cellScannerIndex++;
959 if (cellScannerIndex < this.cells.length) {
960 return true;
961 } else if (cellScannerIndex == this.cells.length) {
962 return false;
963 }
964 throw new NoSuchElementException("Cannot advance beyond the last cell");
965 }
966
967 public Boolean getExists() {
968 return exists;
969 }
970
971 public void setExists(Boolean exists) {
972 checkReadonly();
973 this.exists = exists;
974 }
975
976 /**
977 * Whether or not the results are coming from possibly stale data. Stale results
978 * might be returned if {@link Consistency} is not STRONG for the query.
979 * @return Whether or not the results are coming from possibly stale data.
980 */
981 public boolean isStale() {
982 return stale;
983 }
984
985 /**
986 * Whether or not the result is a partial result. Partial results contain a subset of the cells
987 * for a row and should be combined with a result representing the remaining cells in that row to
988 * form a complete (non-partial) result.
989 * @return Whether or not the result is a partial result
990 */
991 public boolean isPartial() {
992 return partial;
993 }
994
995 /**
996 * Add load information about the region to the information about the result
997 * @param loadStats statistics about the current region from which this was returned
998 * @deprecated use {@link #setStatistics(ClientProtos.RegionLoadStats)} instead
999 * @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
1000 * (which is supposed to be immutable).
1001 */
1002 @InterfaceAudience.Private
1003 @Deprecated
1004 public void addResults(ClientProtos.RegionLoadStats loadStats) {
1005 checkReadonly();
1006 this.stats = loadStats;
1007 }
1008
1009 /**
1010 * Set load information about the region to the information about the result
1011 * @param loadStats statistics about the current region from which this was returned
1012 */
1013 @InterfaceAudience.Private
1014 public void setStatistics(ClientProtos.RegionLoadStats loadStats) {
1015 this.stats = loadStats;
1016 }
1017
1018 /**
1019 * @return the associated statistics about the region from which this was returned. Can be
1020 * <tt>null</tt> if stats are disabled.
1021 */
1022 public ClientProtos.RegionLoadStats getStats() {
1023 return stats;
1024 }
1025
1026 /**
1027 * All methods modifying state of Result object must call this method
1028 * to ensure that special purpose immutable Results can't be accidentally modified.
1029 */
1030 private void checkReadonly() {
1031 if (readonly == true) {
1032 throw new UnsupportedOperationException("Attempting to modify readonly EMPTY_RESULT!");
1033 }
1034 }
1035 }