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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 package org
.apache
.hadoop
.hbase
.client
;
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
.Collections
;
28 import java
.util
.Comparator
;
29 import java
.util
.Iterator
;
30 import java
.util
.List
;
32 import java
.util
.NavigableMap
;
33 import java
.util
.NoSuchElementException
;
34 import java
.util
.TreeMap
;
36 import org
.apache
.hadoop
.hbase
.Cell
;
37 import org
.apache
.hadoop
.hbase
.CellComparator
;
38 import org
.apache
.hadoop
.hbase
.CellScannable
;
39 import org
.apache
.hadoop
.hbase
.CellScanner
;
40 import org
.apache
.hadoop
.hbase
.CellUtil
;
41 import org
.apache
.hadoop
.hbase
.HConstants
;
42 import org
.apache
.hadoop
.hbase
.PrivateCellUtil
;
43 import org
.apache
.hadoop
.hbase
.KeyValue
;
44 import org
.apache
.hadoop
.hbase
.KeyValueUtil
;
45 import org
.apache
.yetus
.audience
.InterfaceAudience
;
46 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
49 * Single row result of a {@link Get} or {@link Scan} query.<p>
51 * This class is <b>NOT THREAD SAFE</b>.<p>
53 * Convenience methods are available that return various {@link Map}
54 * structures and values directly.<p>
56 * To get a complete mapping of all cells in the Result, which can include
57 * multiple families and multiple versions, use {@link #getMap()}.<p>
59 * To get a mapping of each family to its columns (qualifiers and values),
60 * including only the latest version of each, use {@link #getNoVersionMap()}.
62 * To get a mapping of qualifiers to latest values for an individual family use
63 * {@link #getFamilyMap(byte[])}.<p>
65 * To get the latest value for a specific family and qualifier use
66 * {@link #getValue(byte[], byte[])}.
68 * A Result is backed by an array of {@link Cell} objects, each representing
69 * an HBase cell defined by the row, family, qualifier, timestamp, and value.<p>
71 * The underlying {@link Cell} objects can be accessed through the method {@link #listCells()}.
72 * This will create a List from the internal Cell []. Better is to exploit the fact that
73 * a new Result instance is a primed {@link CellScanner}; just call {@link #advance()} and
74 * {@link #current()} to iterate over Cells as you would any {@link CellScanner}.
75 * Call {@link #cellScanner()} to reset should you need to iterate the same Result over again
76 * ({@link CellScanner}s are one-shot).
78 * If you need to overwrite a Result with another Result instance -- as in the old 'mapred'
79 * RecordReader next invocations -- then create an empty Result with the null constructor and
80 * in then use {@link #copyFrom(Result)}
82 @InterfaceAudience.Public
83 public class Result
implements CellScannable
, CellScanner
{
85 private Boolean exists
; // if the query was just to check existence.
86 private boolean stale
= false;
89 * See {@link #mayHaveMoreCellsInRow()}.
91 private boolean mayHaveMoreCellsInRow
= false;
92 // We're not using java serialization. Transient here is just a marker to say
93 // that this is where we cache row if we're ever asked for it.
94 private transient byte [] row
= null;
95 // Ditto for familyMap. It can be composed on fly from passed in kvs.
96 private transient NavigableMap
<byte[], NavigableMap
<byte[], NavigableMap
<Long
, byte[]>>>
99 private static ThreadLocal
<byte[]> localBuffer
= new ThreadLocal
<>();
100 private static final int PAD_WIDTH
= 128;
101 public static final Result EMPTY_RESULT
= new Result(true);
103 private final static int INITIAL_CELLSCANNER_INDEX
= -1;
106 * Index for where we are when Result is acting as a {@link CellScanner}.
108 private int cellScannerIndex
= INITIAL_CELLSCANNER_INDEX
;
109 private RegionLoadStats stats
;
111 private final boolean readonly
;
113 private Cursor cursor
= null;
116 * Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
117 * Use this to represent no results if {@code null} won't do or in old 'mapred' as opposed
118 * to 'mapreduce' package MapReduce where you need to overwrite a Result instance with a
119 * {@link #copyFrom(Result)} call.
126 * Allows to construct special purpose immutable Result objects,
127 * such as EMPTY_RESULT.
128 * @param readonly whether this Result instance is readonly
130 private Result(boolean readonly
) {
131 this.readonly
= readonly
;
135 * Instantiate a Result with the specified List of KeyValues.
136 * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
137 * @param cells List of cells
139 public static Result
create(List
<Cell
> cells
) {
140 return create(cells
, null);
143 public static Result
create(List
<Cell
> cells
, Boolean exists
) {
144 return create(cells
, exists
, false);
147 public static Result
create(List
<Cell
> cells
, Boolean exists
, boolean stale
) {
148 return create(cells
, exists
, stale
, false);
151 public static Result
create(List
<Cell
> cells
, Boolean exists
, boolean stale
,
152 boolean mayHaveMoreCellsInRow
) {
154 return new Result(null, exists
, stale
, mayHaveMoreCellsInRow
);
156 return new Result(cells
.toArray(new Cell
[cells
.size()]), null, stale
, mayHaveMoreCellsInRow
);
160 * Instantiate a Result with the specified array of KeyValues.
161 * <br><strong>Note:</strong> You must ensure that the keyvalues are already sorted.
162 * @param cells array of cells
164 public static Result
create(Cell
[] cells
) {
165 return create(cells
, null, false);
168 public static Result
create(Cell
[] cells
, Boolean exists
, boolean stale
) {
169 return create(cells
, exists
, stale
, false);
172 public static Result
create(Cell
[] cells
, Boolean exists
, boolean stale
,
173 boolean mayHaveMoreCellsInRow
) {
174 if (exists
!= null) {
175 return new Result(null, exists
, stale
, mayHaveMoreCellsInRow
);
177 return new Result(cells
, null, stale
, mayHaveMoreCellsInRow
);
180 public static Result
createCursorResult(Cursor cursor
) {
181 return new Result(cursor
);
184 private Result(Cursor cursor
) {
185 this.cursor
= cursor
;
186 this.readonly
= false;
189 /** Private ctor. Use {@link #create(Cell[])}. */
190 private Result(Cell
[] cells
, Boolean exists
, boolean stale
, boolean mayHaveMoreCellsInRow
) {
192 this.exists
= exists
;
194 this.mayHaveMoreCellsInRow
= mayHaveMoreCellsInRow
;
195 this.readonly
= false;
199 * Method for retrieving the row key that corresponds to
200 * the row from which this Result was created.
203 public byte [] getRow() {
204 if (this.row
== null) {
205 this.row
= (this.cells
== null || this.cells
.length
== 0) ?
207 CellUtil
.cloneRow(this.cells
[0]);
213 * Return the array of Cells backing this Result instance.
215 * The array is sorted from smallest -> largest using the
216 * {@link CellComparator}.
218 * The array only contains what your Get or Scan specifies and no more.
219 * For example if you request column "A" 1 version you will have at most 1
220 * Cell in the array. If you request column "A" with 2 version you will
221 * have at most 2 Cells, with the first one being the newer timestamp and
222 * the second being the older timestamp (this is the sort order defined by
223 * {@link CellComparator}). If columns don't exist, they won't be
224 * present in the result. Therefore if you ask for 1 version all columns,
225 * it is safe to iterate over this array and expect to see 1 Cell for
226 * each column and no more.
228 * This API is faster than using getFamilyMap() and getMap()
230 * @return array of Cells; can be null if nothing in the result
232 public Cell
[] rawCells() {
237 * Create a sorted list of the Cell's in this result.
239 * Since HBase 0.20.5 this is equivalent to raw().
241 * @return sorted List of Cells; can be null if no cells in the result
243 public List
<Cell
> listCells() {
244 return isEmpty()?
null: Arrays
.asList(rawCells());
248 * Return the Cells for the specific column. The Cells are sorted in
249 * the {@link CellComparator} order. That implies the first entry in
250 * the list is the most recent column. If the query (Scan or Get) only
251 * requested 1 version the list will contain at most 1 entry. If the column
252 * did not exist in the result set (either the column does not exist
253 * or the column was not selected in the query) the list will be empty.
255 * Also see getColumnLatest which returns just a Cell
257 * @param family the family
259 * @return a list of Cells for this column or empty list if the column
260 * did not exist in the result set
262 public List
<Cell
> getColumnCells(byte [] family
, byte [] qualifier
) {
263 List
<Cell
> result
= new ArrayList
<>();
265 Cell
[] kvs
= rawCells();
267 if (kvs
== null || kvs
.length
== 0) {
270 int pos
= binarySearch(kvs
, family
, qualifier
);
272 return result
; // cant find it
275 for (int i
= pos
; i
< kvs
.length
; i
++) {
276 if (CellUtil
.matchingColumn(kvs
[i
], family
,qualifier
)) {
286 private byte[] notNullBytes(final byte[] bytes
) {
288 return HConstants
.EMPTY_BYTE_ARRAY
;
294 protected int binarySearch(final Cell
[] kvs
,
295 final byte [] family
,
296 final byte [] qualifier
) {
297 byte[] familyNotNull
= notNullBytes(family
);
298 byte[] qualifierNotNull
= notNullBytes(qualifier
);
300 PrivateCellUtil
.createFirstOnRow(kvs
[0].getRowArray(),
301 kvs
[0].getRowOffset(), kvs
[0].getRowLength(),
302 familyNotNull
, 0, (byte)familyNotNull
.length
,
303 qualifierNotNull
, 0, qualifierNotNull
.length
);
305 // pos === ( -(insertion point) - 1)
306 int pos
= Arrays
.binarySearch(kvs
, searchTerm
, CellComparator
.getInstance());
307 // never will exact match
310 // pos is now insertion point
312 if (pos
== kvs
.length
) {
313 return -1; // doesn't exist
319 * Searches for the latest value for the specified column.
321 * @param kvs the array to search
322 * @param family family name
323 * @param foffset family offset
324 * @param flength family length
325 * @param qualifier column qualifier
326 * @param qoffset qualifier offset
327 * @param qlength qualifier length
329 * @return the index where the value was found, or -1 otherwise
331 protected int binarySearch(final Cell
[] kvs
,
332 final byte [] family
, final int foffset
, final int flength
,
333 final byte [] qualifier
, final int qoffset
, final int qlength
) {
335 double keyValueSize
= (double)
336 KeyValue
.getKeyValueDataStructureSize(kvs
[0].getRowLength(), flength
, qlength
, 0);
338 byte[] buffer
= localBuffer
.get();
339 if (buffer
== null || keyValueSize
> buffer
.length
) {
340 // pad to the smallest multiple of the pad width
341 buffer
= new byte[(int) Math
.ceil(keyValueSize
/ PAD_WIDTH
) * PAD_WIDTH
];
342 localBuffer
.set(buffer
);
345 Cell searchTerm
= KeyValueUtil
.createFirstOnRow(buffer
, 0,
346 kvs
[0].getRowArray(), kvs
[0].getRowOffset(), kvs
[0].getRowLength(),
347 family
, foffset
, flength
,
348 qualifier
, qoffset
, qlength
);
350 // pos === ( -(insertion point) - 1)
351 int pos
= Arrays
.binarySearch(kvs
, searchTerm
, CellComparator
.getInstance());
352 // never will exact match
355 // pos is now insertion point
357 if (pos
== kvs
.length
) {
358 return -1; // doesn't exist
364 * The Cell for the most recent timestamp for a given column.
369 * @return the Cell for the column, or null if no value exists in the row or none have been
370 * selected in the query (Get/Scan)
372 public Cell
getColumnLatestCell(byte [] family
, byte [] qualifier
) {
373 Cell
[] kvs
= rawCells(); // side effect possibly.
374 if (kvs
== null || kvs
.length
== 0) {
377 int pos
= binarySearch(kvs
, family
, qualifier
);
381 if (CellUtil
.matchingColumn(kvs
[pos
], family
, qualifier
)) {
388 * The Cell for the most recent timestamp for a given column.
390 * @param family family name
391 * @param foffset family offset
392 * @param flength family length
393 * @param qualifier column qualifier
394 * @param qoffset qualifier offset
395 * @param qlength qualifier length
397 * @return the Cell for the column, or null if no value exists in the row or none have been
398 * selected in the query (Get/Scan)
400 public Cell
getColumnLatestCell(byte [] family
, int foffset
, int flength
,
401 byte [] qualifier
, int qoffset
, int qlength
) {
403 Cell
[] kvs
= rawCells(); // side effect possibly.
404 if (kvs
== null || kvs
.length
== 0) {
407 int pos
= binarySearch(kvs
, family
, foffset
, flength
, qualifier
, qoffset
, qlength
);
411 if (PrivateCellUtil
.matchingColumn(kvs
[pos
], family
, foffset
, flength
, qualifier
, qoffset
,
419 * Get the latest version of the specified column.
420 * Note: this call clones the value content of the hosting Cell. See
421 * {@link #getValueAsByteBuffer(byte[], byte[])}, etc., or {@link #listCells()} if you would
423 * @param family family name
424 * @param qualifier column qualifier
425 * @return value of latest version of column, null if none found
427 public byte[] getValue(byte [] family
, byte [] qualifier
) {
428 Cell kv
= getColumnLatestCell(family
, qualifier
);
432 return CellUtil
.cloneValue(kv
);
436 * Returns the value wrapped in a new <code>ByteBuffer</code>.
438 * @param family family name
439 * @param qualifier column qualifier
441 * @return the latest version of the column, or <code>null</code> if none found
443 public ByteBuffer
getValueAsByteBuffer(byte [] family
, byte [] qualifier
) {
445 Cell kv
= getColumnLatestCell(family
, 0, family
.length
, qualifier
, 0, qualifier
.length
);
450 return ByteBuffer
.wrap(kv
.getValueArray(), kv
.getValueOffset(), kv
.getValueLength()).
455 * Returns the value wrapped in a new <code>ByteBuffer</code>.
457 * @param family family name
458 * @param foffset family offset
459 * @param flength family length
460 * @param qualifier column qualifier
461 * @param qoffset qualifier offset
462 * @param qlength qualifier length
464 * @return the latest version of the column, or <code>null</code> if none found
466 public ByteBuffer
getValueAsByteBuffer(byte [] family
, int foffset
, int flength
,
467 byte [] qualifier
, int qoffset
, int qlength
) {
469 Cell kv
= getColumnLatestCell(family
, foffset
, flength
, qualifier
, qoffset
, qlength
);
474 return ByteBuffer
.wrap(kv
.getValueArray(), kv
.getValueOffset(), kv
.getValueLength()).
479 * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
481 * Does not clear or flip the buffer.
483 * @param family family name
484 * @param qualifier column qualifier
485 * @param dst the buffer where to write the value
487 * @return <code>true</code> if a value was found, <code>false</code> otherwise
489 * @throws BufferOverflowException there is insufficient space remaining in the buffer
491 public boolean loadValue(byte [] family
, byte [] qualifier
, ByteBuffer dst
)
492 throws BufferOverflowException
{
493 return loadValue(family
, 0, family
.length
, qualifier
, 0, qualifier
.length
, dst
);
497 * Loads the latest version of the specified column into the provided <code>ByteBuffer</code>.
499 * Does not clear or flip the buffer.
501 * @param family family name
502 * @param foffset family offset
503 * @param flength family length
504 * @param qualifier column qualifier
505 * @param qoffset qualifier offset
506 * @param qlength qualifier length
507 * @param dst the buffer where to write the value
509 * @return <code>true</code> if a value was found, <code>false</code> otherwise
511 * @throws BufferOverflowException there is insufficient space remaining in the buffer
513 public boolean loadValue(byte [] family
, int foffset
, int flength
,
514 byte [] qualifier
, int qoffset
, int qlength
, ByteBuffer dst
)
515 throws BufferOverflowException
{
516 Cell kv
= getColumnLatestCell(family
, foffset
, flength
, qualifier
, qoffset
, qlength
);
521 dst
.put(kv
.getValueArray(), kv
.getValueOffset(), kv
.getValueLength());
526 * Checks if the specified column contains a non-empty value (not a zero-length byte array).
528 * @param family family name
529 * @param qualifier column qualifier
531 * @return whether or not a latest value exists and is not empty
533 public boolean containsNonEmptyColumn(byte [] family
, byte [] qualifier
) {
535 return containsNonEmptyColumn(family
, 0, family
.length
, qualifier
, 0, qualifier
.length
);
539 * Checks if the specified column contains a non-empty value (not a zero-length byte array).
541 * @param family family name
542 * @param foffset family offset
543 * @param flength family length
544 * @param qualifier column qualifier
545 * @param qoffset qualifier offset
546 * @param qlength qualifier length
548 * @return whether or not a latest value exists and is not empty
550 public boolean containsNonEmptyColumn(byte [] family
, int foffset
, int flength
,
551 byte [] qualifier
, int qoffset
, int qlength
) {
553 Cell kv
= getColumnLatestCell(family
, foffset
, flength
, qualifier
, qoffset
, qlength
);
555 return (kv
!= null) && (kv
.getValueLength() > 0);
559 * Checks if the specified column contains an empty value (a zero-length byte array).
561 * @param family family name
562 * @param qualifier column qualifier
564 * @return whether or not a latest value exists and is empty
566 public boolean containsEmptyColumn(byte [] family
, byte [] qualifier
) {
568 return containsEmptyColumn(family
, 0, family
.length
, qualifier
, 0, qualifier
.length
);
572 * Checks if the specified column contains an empty value (a zero-length byte array).
574 * @param family family name
575 * @param foffset family offset
576 * @param flength family length
577 * @param qualifier column qualifier
578 * @param qoffset qualifier offset
579 * @param qlength qualifier length
581 * @return whether or not a latest value exists and is empty
583 public boolean containsEmptyColumn(byte [] family
, int foffset
, int flength
,
584 byte [] qualifier
, int qoffset
, int qlength
) {
585 Cell kv
= getColumnLatestCell(family
, foffset
, flength
, qualifier
, qoffset
, qlength
);
587 return (kv
!= null) && (kv
.getValueLength() == 0);
591 * Checks for existence of a value for the specified column (empty or not).
593 * @param family family name
594 * @param qualifier column qualifier
596 * @return true if at least one value exists in the result, false if not
598 public boolean containsColumn(byte [] family
, byte [] qualifier
) {
599 Cell kv
= getColumnLatestCell(family
, qualifier
);
604 * Checks for existence of a value for the specified column (empty or not).
606 * @param family family name
607 * @param foffset family offset
608 * @param flength family length
609 * @param qualifier column qualifier
610 * @param qoffset qualifier offset
611 * @param qlength qualifier length
613 * @return true if at least one value exists in the result, false if not
615 public boolean containsColumn(byte [] family
, int foffset
, int flength
,
616 byte [] qualifier
, int qoffset
, int qlength
) {
618 return getColumnLatestCell(family
, foffset
, flength
, qualifier
, qoffset
, qlength
) != null;
622 * Map of families to all versions of its qualifiers and values.
624 * Returns a three level Map of the form:
625 * <code>Map&family,Map<qualifier,Map<timestamp,value>>></code>
627 * Note: All other map returning methods make use of this map internally.
628 * @return map from families to qualifiers to versions
630 public NavigableMap
<byte[], NavigableMap
<byte[], NavigableMap
<Long
, byte[]>>> getMap() {
631 if (this.familyMap
!= null) {
632 return this.familyMap
;
637 this.familyMap
= new TreeMap
<>(Bytes
.BYTES_COMPARATOR
);
638 for(Cell kv
: this.cells
) {
639 byte [] family
= CellUtil
.cloneFamily(kv
);
640 NavigableMap
<byte[], NavigableMap
<Long
, byte[]>> columnMap
= familyMap
.get(family
);
641 if(columnMap
== null) {
642 columnMap
= new TreeMap
<>(Bytes
.BYTES_COMPARATOR
);
643 familyMap
.put(family
, columnMap
);
645 byte [] qualifier
= CellUtil
.cloneQualifier(kv
);
646 NavigableMap
<Long
, byte[]> versionMap
= columnMap
.get(qualifier
);
647 if(versionMap
== null) {
648 versionMap
= new TreeMap
<>(new Comparator
<Long
>() {
650 public int compare(Long l1
, Long l2
) {
651 return l2
.compareTo(l1
);
654 columnMap
.put(qualifier
, versionMap
);
656 Long timestamp
= kv
.getTimestamp();
657 byte [] value
= CellUtil
.cloneValue(kv
);
659 versionMap
.put(timestamp
, value
);
661 return this.familyMap
;
665 * Map of families to their most recent qualifiers and values.
667 * Returns a two level Map of the form: <code>Map&family,Map<qualifier,value>></code>
669 * The most recent version of each qualifier will be used.
670 * @return map from families to qualifiers and value
672 public NavigableMap
<byte[], NavigableMap
<byte[], byte[]>> getNoVersionMap() {
673 if(this.familyMap
== null) {
679 NavigableMap
<byte[], NavigableMap
<byte[], byte[]>> returnMap
= new TreeMap
<>(Bytes
.BYTES_COMPARATOR
);
680 for(Map
.Entry
<byte[], NavigableMap
<byte[], NavigableMap
<Long
, byte[]>>>
681 familyEntry
: familyMap
.entrySet()) {
682 NavigableMap
<byte[], byte[]> qualifierMap
= new TreeMap
<>(Bytes
.BYTES_COMPARATOR
);
683 for(Map
.Entry
<byte[], NavigableMap
<Long
, byte[]>> qualifierEntry
:
684 familyEntry
.getValue().entrySet()) {
686 qualifierEntry
.getValue().get(qualifierEntry
.getValue().firstKey());
687 qualifierMap
.put(qualifierEntry
.getKey(), value
);
689 returnMap
.put(familyEntry
.getKey(), qualifierMap
);
695 * Map of qualifiers to values.
697 * Returns a Map of the form: <code>Map<qualifier,value></code>
698 * @param family column family to get
699 * @return map of qualifiers to values
701 public NavigableMap
<byte[], byte[]> getFamilyMap(byte [] family
) {
702 if(this.familyMap
== null) {
708 NavigableMap
<byte[], byte[]> returnMap
= new TreeMap
<>(Bytes
.BYTES_COMPARATOR
);
709 NavigableMap
<byte[], NavigableMap
<Long
, byte[]>> qualifierMap
=
710 familyMap
.get(family
);
711 if(qualifierMap
== null) {
714 for(Map
.Entry
<byte[], NavigableMap
<Long
, byte[]>> entry
:
715 qualifierMap
.entrySet()) {
717 entry
.getValue().get(entry
.getValue().firstKey());
718 returnMap
.put(entry
.getKey(), value
);
724 * Returns the value of the first column in the Result.
725 * @return value of the first column
727 public byte [] value() {
731 return CellUtil
.cloneValue(cells
[0]);
735 * Check if the underlying Cell [] is empty or not
736 * @return true if empty
738 public boolean isEmpty() {
739 return this.cells
== null || this.cells
.length
== 0;
743 * @return the size of the underlying Cell []
746 return this.cells
== null?
0: this.cells
.length
;
753 public String
toString() {
754 StringBuilder sb
= new StringBuilder();
755 sb
.append("keyvalues=");
758 return sb
.toString();
761 boolean moreThanOne
= false;
762 for(Cell kv
: this.cells
) {
768 sb
.append(kv
.toString());
771 return sb
.toString();
775 * Does a deep comparison of two Results, down to the byte arrays.
776 * @param res1 first result to compare
777 * @param res2 second result to compare
778 * @throws Exception Every difference is throwing an exception
780 public static void compareResults(Result res1
, Result res2
)
782 compareResults(res1
, res2
, true);
786 * Does a deep comparison of two Results, down to the byte arrays.
787 * @param res1 first result to compare
788 * @param res2 second result to compare
789 * @param verbose includes string representation for all cells in the exception if true;
790 * otherwise include rowkey only
791 * @throws Exception Every difference is throwing an exception
793 public static void compareResults(Result res1
, Result res2
, boolean verbose
)
796 throw new Exception("There wasn't enough rows, we stopped at "
797 + Bytes
.toStringBinary(res1
.getRow()));
799 if (res1
.size() != res2
.size()) {
802 "This row doesn't have the same number of KVs: "
803 + res1
+ " compared to " + res2
);
806 "This row doesn't have the same number of KVs: row="
807 + Bytes
.toStringBinary(res1
.getRow())
808 + ", " + res1
.size() + " cells are compared to " + res2
.size() + " cells");
811 Cell
[] ourKVs
= res1
.rawCells();
812 Cell
[] replicatedKVs
= res2
.rawCells();
813 for (int i
= 0; i
< res1
.size(); i
++) {
814 if (!ourKVs
[i
].equals(replicatedKVs
[i
]) ||
815 !CellUtil
.matchingValue(ourKVs
[i
], replicatedKVs
[i
]) ||
816 !CellUtil
.matchingTags(ourKVs
[i
], replicatedKVs
[i
])) {
818 throw new Exception("This result was different: "
819 + res1
+ " compared to " + res2
);
821 throw new Exception("This result was different: row="
822 + Bytes
.toStringBinary(res1
.getRow()));
829 * Forms a single result from the partial results in the partialResults list. This method is
830 * useful for reconstructing partial results on the client side.
831 * @param partialResults list of partial results
832 * @return The complete result that is formed by combining all of the partial results together
833 * @throws IOException A complete result cannot be formed because the results in the partial list
834 * come from different rows
836 public static Result
createCompleteResult(Iterable
<Result
> partialResults
)
838 if (partialResults
== null) {
839 return Result
.create(Collections
.emptyList(), null, false);
841 List
<Cell
> cells
= new ArrayList
<>();
842 boolean stale
= false;
843 byte[] prevRow
= null;
844 byte[] currentRow
= null;
845 for (Iterator
<Result
> iter
= partialResults
.iterator(); iter
.hasNext();) {
846 Result r
= iter
.next();
847 currentRow
= r
.getRow();
848 if (prevRow
!= null && !Bytes
.equals(prevRow
, currentRow
)) {
849 throw new IOException(
850 "Cannot form complete result. Rows of partial results do not match." +
851 " Partial Results: " + partialResults
);
853 // Ensure that all Results except the last one are marked as partials. The last result
854 // may not be marked as a partial because Results are only marked as partials when
855 // the scan on the server side must be stopped due to reaching the maxResultSize.
856 // Visualizing it makes it easier to understand:
857 // maxResultSize: 2 cells
858 // (-x-) represents cell number x in a row
859 // Example: row1: -1- -2- -3- -4- -5- (5 cells total)
860 // How row1 will be returned by the server as partial Results:
861 // Result1: -1- -2- (2 cells, size limit reached, mark as partial)
862 // Result2: -3- -4- (2 cells, size limit reached, mark as partial)
863 // Result3: -5- (1 cell, size limit NOT reached, NOT marked as partial)
864 if (iter
.hasNext() && !r
.mayHaveMoreCellsInRow()) {
865 throw new IOException("Cannot form complete result. Result is missing partial flag. " +
866 "Partial Results: " + partialResults
);
868 prevRow
= currentRow
;
869 stale
= stale
|| r
.isStale();
870 for (Cell c
: r
.rawCells()) {
875 return Result
.create(cells
, null, stale
);
879 * Get total size of raw cells
881 * @return Total size.
883 public static long getTotalSizeOfCells(Result result
) {
885 if (result
.isEmpty()) {
888 for (Cell c
: result
.rawCells()) {
889 size
+= c
.heapSize();
895 * Copy another Result into this one. Needed for the old Mapred framework
896 * @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
897 * (which is supposed to be immutable).
900 public void copyFrom(Result other
) {
903 this.familyMap
= null;
904 this.cells
= other
.cells
;
908 public CellScanner
cellScanner() {
910 this.cellScannerIndex
= INITIAL_CELLSCANNER_INDEX
;
915 public Cell
current() {
917 || cellScannerIndex
== INITIAL_CELLSCANNER_INDEX
918 || cellScannerIndex
>= cells
.length
) {
921 return this.cells
[cellScannerIndex
];
925 public boolean advance() {
930 if (cellScannerIndex
< this.cells
.length
) {
932 } else if (cellScannerIndex
== this.cells
.length
) {
935 throw new NoSuchElementException("Cannot advance beyond the last cell");
938 public Boolean
getExists() {
942 public void setExists(Boolean exists
) {
944 this.exists
= exists
;
948 * Whether or not the results are coming from possibly stale data. Stale results
949 * might be returned if {@link Consistency} is not STRONG for the query.
950 * @return Whether or not the results are coming from possibly stale data.
952 public boolean isStale() {
957 * For scanning large rows, the RS may choose to return the cells chunk by chunk to prevent OOM
958 * or timeout. This flag is used to tell you if the current Result is the last one of the current
959 * row. False means this Result is the last one. True means there MAY be more cells belonging to
961 * If you don't use {@link Scan#setAllowPartialResults(boolean)} or {@link Scan#setBatch(int)},
962 * this method will always return false because the Result must contains all cells in one Row.
964 public boolean mayHaveMoreCellsInRow() {
965 return mayHaveMoreCellsInRow
;
969 * Set load information about the region to the information about the result
970 * @param loadStats statistics about the current region from which this was returned
972 @InterfaceAudience.Private
973 public void setStatistics(RegionLoadStats loadStats
) {
974 this.stats
= loadStats
;
978 * @return the associated statistics about the region from which this was returned. Can be
979 * <tt>null</tt> if stats are disabled.
981 public RegionLoadStats
getStats() {
986 * All methods modifying state of Result object must call this method
987 * to ensure that special purpose immutable Results can't be accidentally modified.
989 private void checkReadonly() {
990 if (readonly
== true) {
991 throw new UnsupportedOperationException("Attempting to modify readonly EMPTY_RESULT!");
996 * Return true if this Result is a cursor to tell users where the server has scanned.
997 * In this Result the only meaningful method is {@link #getCursor()}.
1000 * while (r = scanner.next() && r != null) {
1002 * // scanning is not end, it is a cursor, save its row key and close scanner if you want, or
1003 * // just continue the loop to call next().
1005 * // just like before
1008 * // scanning is end
1011 * {@link Scan#setNeedCursorResult(boolean)}
1013 * {@link #getCursor()}
1015 public boolean isCursor() {
1016 return cursor
!= null ;
1020 * Return the cursor if this Result is a cursor result.
1021 * {@link Scan#setNeedCursorResult(boolean)}
1023 * {@link #isCursor()}
1025 public Cursor
getCursor(){