1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.client;
22
23 import java.io.DataInput;
24 import java.io.DataOutput;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Comparator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.NavigableMap;
32 import java.util.TreeMap;
33
34 import org.apache.hadoop.hbase.KeyValue;
35 import org.apache.hadoop.hbase.KeyValue.SplitKeyValue;
36 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
37 import org.apache.hadoop.hbase.io.WritableWithSize;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.io.DataOutputBuffer;
40 import org.apache.hadoop.io.Writable;
41
42
43
44
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 public class Result implements Writable, WritableWithSize {
70 private static final byte RESULT_VERSION = (byte)1;
71
72 private KeyValue [] kvs = null;
73 private NavigableMap<byte[],
74 NavigableMap<byte[], NavigableMap<Long, byte[]>>> familyMap = null;
75
76
77 private transient byte [] row = null;
78 private ImmutableBytesWritable bytes = null;
79
80
81
82
83 public Result() {}
84
85
86
87
88
89 public Result(KeyValue [] kvs) {
90 if(kvs != null && kvs.length > 0) {
91 this.kvs = kvs;
92 }
93 }
94
95
96
97
98
99 public Result(List<KeyValue> kvs) {
100 this(kvs.toArray(new KeyValue[kvs.size()]));
101 }
102
103
104
105
106
107 public Result(ImmutableBytesWritable bytes) {
108 this.bytes = bytes;
109 }
110
111
112
113
114
115
116 public byte [] getRow() {
117 if (this.row == null) {
118 if(this.kvs == null) {
119 readFields();
120 }
121 this.row = this.kvs.length == 0? null: this.kvs[0].getRow();
122 }
123 return this.row;
124 }
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146 public KeyValue[] raw() {
147 if(this.kvs == null) {
148 readFields();
149 }
150 return kvs;
151 }
152
153
154
155
156
157
158
159
160 public List<KeyValue> list() {
161 if(this.kvs == null) {
162 readFields();
163 }
164 return isEmpty()? null: Arrays.asList(raw());
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 public List<KeyValue> getColumn(byte [] family, byte [] qualifier) {
183 List<KeyValue> result = new ArrayList<KeyValue>();
184
185 KeyValue [] kvs = raw();
186
187 if (kvs == null || kvs.length == 0) {
188 return result;
189 }
190 int pos = binarySearch(kvs, family, qualifier);
191 if (pos == -1) {
192 return result;
193 }
194
195 for (int i = pos ; i < kvs.length ; i++ ) {
196 KeyValue kv = kvs[i];
197 if (kv.matchingColumn(family,qualifier)) {
198 result.add(kv);
199 } else {
200 break;
201 }
202 }
203
204 return result;
205 }
206
207 protected int binarySearch(final KeyValue [] kvs,
208 final byte [] family,
209 final byte [] qualifier) {
210 KeyValue searchTerm =
211 KeyValue.createFirstOnRow(kvs[0].getRow(),
212 family, qualifier);
213
214
215 int pos = Arrays.binarySearch(kvs, searchTerm, KeyValue.COMPARATOR);
216
217 if (pos < 0) {
218 pos = (pos+1) * -1;
219
220 }
221 if (pos == kvs.length) {
222 return -1;
223 }
224 return pos;
225 }
226
227
228
229
230
231
232
233
234
235
236 public KeyValue getColumnLatest(byte [] family, byte [] qualifier) {
237 KeyValue [] kvs = raw();
238 if (kvs == null || kvs.length == 0) {
239 return null;
240 }
241 int pos = binarySearch(kvs, family, qualifier);
242 if (pos == -1) {
243 return null;
244 }
245 KeyValue kv = kvs[pos];
246 if (kv.matchingColumn(family, qualifier)) {
247 return kv;
248 }
249 return null;
250 }
251
252
253
254
255
256
257
258 public byte[] getValue(byte [] family, byte [] qualifier) {
259 KeyValue kv = getColumnLatest(family, qualifier);
260 if (kv == null) {
261 return null;
262 }
263 return kv.getValue();
264 }
265
266
267
268
269
270
271
272 public boolean containsColumn(byte [] family, byte [] qualifier) {
273 KeyValue kv = getColumnLatest(family, qualifier);
274 return kv != null;
275 }
276
277
278
279
280
281
282
283
284
285
286 public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> getMap() {
287 if(this.familyMap != null) {
288 return this.familyMap;
289 }
290 if(isEmpty()) {
291 return null;
292 }
293 this.familyMap =
294 new TreeMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
295 (Bytes.BYTES_COMPARATOR);
296 for(KeyValue kv : this.kvs) {
297 SplitKeyValue splitKV = kv.split();
298 byte [] family = splitKV.getFamily();
299 NavigableMap<byte[], NavigableMap<Long, byte[]>> columnMap =
300 familyMap.get(family);
301 if(columnMap == null) {
302 columnMap = new TreeMap<byte[], NavigableMap<Long, byte[]>>
303 (Bytes.BYTES_COMPARATOR);
304 familyMap.put(family, columnMap);
305 }
306 byte [] qualifier = splitKV.getQualifier();
307 NavigableMap<Long, byte[]> versionMap = columnMap.get(qualifier);
308 if(versionMap == null) {
309 versionMap = new TreeMap<Long, byte[]>(new Comparator<Long>() {
310 public int compare(Long l1, Long l2) {
311 return l2.compareTo(l1);
312 }
313 });
314 columnMap.put(qualifier, versionMap);
315 }
316 Long timestamp = Bytes.toLong(splitKV.getTimestamp());
317 byte [] value = splitKV.getValue();
318 versionMap.put(timestamp, value);
319 }
320 return this.familyMap;
321 }
322
323
324
325
326
327
328
329
330
331 public NavigableMap<byte[], NavigableMap<byte[], byte[]>> getNoVersionMap() {
332 if(this.familyMap == null) {
333 getMap();
334 }
335 if(isEmpty()) {
336 return null;
337 }
338 NavigableMap<byte[], NavigableMap<byte[], byte[]>> returnMap =
339 new TreeMap<byte[], NavigableMap<byte[], byte[]>>(Bytes.BYTES_COMPARATOR);
340 for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>
341 familyEntry : familyMap.entrySet()) {
342 NavigableMap<byte[], byte[]> qualifierMap =
343 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
344 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> qualifierEntry :
345 familyEntry.getValue().entrySet()) {
346 byte [] value =
347 qualifierEntry.getValue().get(qualifierEntry.getValue().firstKey());
348 qualifierMap.put(qualifierEntry.getKey(), value);
349 }
350 returnMap.put(familyEntry.getKey(), qualifierMap);
351 }
352 return returnMap;
353 }
354
355
356
357
358
359
360
361
362 public NavigableMap<byte[], byte[]> getFamilyMap(byte [] family) {
363 if(this.familyMap == null) {
364 getMap();
365 }
366 if(isEmpty()) {
367 return null;
368 }
369 NavigableMap<byte[], byte[]> returnMap =
370 new TreeMap<byte[], byte[]>(Bytes.BYTES_COMPARATOR);
371 NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifierMap =
372 familyMap.get(family);
373 if(qualifierMap == null) {
374 return returnMap;
375 }
376 for(Map.Entry<byte[], NavigableMap<Long, byte[]>> entry :
377 qualifierMap.entrySet()) {
378 byte [] value =
379 entry.getValue().get(entry.getValue().firstKey());
380 returnMap.put(entry.getKey(), value);
381 }
382 return returnMap;
383 }
384
385
386
387
388
389 public byte [] value() {
390 if (isEmpty()) {
391 return null;
392 }
393 return kvs[0].getValue();
394 }
395
396
397
398
399
400
401
402
403
404 public ImmutableBytesWritable getBytes() {
405 if (this.bytes == null && this.kvs != null) {
406 int totalLen = 0;
407 for(KeyValue kv : kvs) {
408 totalLen += kv.getLength() + Bytes.SIZEOF_INT;
409 }
410 DataOutputBuffer out = new DataOutputBuffer(totalLen);
411 try {
412 for(KeyValue kv : kvs) {
413 kv.write(out);
414 }
415 out.close();
416 } catch (IOException e) {
417 throw new RuntimeException("IOException in Result.getBytes()", e);
418 }
419 this.bytes = new ImmutableBytesWritable(out.getData(), 0, out.getLength());
420 }
421 return this.bytes;
422 }
423
424
425
426
427
428 public boolean isEmpty() {
429 if(this.kvs == null) {
430 readFields();
431 }
432 return this.kvs == null || this.kvs.length == 0;
433 }
434
435
436
437
438 public int size() {
439 if(this.kvs == null) {
440 readFields();
441 }
442 return this.kvs == null? 0: this.kvs.length;
443 }
444
445
446
447
448 @Override
449 public String toString() {
450 StringBuilder sb = new StringBuilder();
451 sb.append("keyvalues=");
452 if(isEmpty()) {
453 sb.append("NONE");
454 return sb.toString();
455 }
456 sb.append("{");
457 boolean moreThanOne = false;
458 for(KeyValue kv : this.kvs) {
459 if(moreThanOne) {
460 sb.append(", ");
461 } else {
462 moreThanOne = true;
463 }
464 sb.append(kv.toString());
465 }
466 sb.append("}");
467 return sb.toString();
468 }
469
470
471 public void readFields(final DataInput in)
472 throws IOException {
473 familyMap = null;
474 row = null;
475 kvs = null;
476 int totalBuffer = in.readInt();
477 if(totalBuffer == 0) {
478 bytes = null;
479 return;
480 }
481 byte [] raw = new byte[totalBuffer];
482 readChunked(in, raw, 0, totalBuffer);
483 bytes = new ImmutableBytesWritable(raw, 0, totalBuffer);
484 }
485
486 private void readChunked(final DataInput in, byte[] dest, int ofs, int len)
487 throws IOException {
488 int maxRead = 8192;
489
490 for (; ofs < len; ofs += maxRead)
491 in.readFully(dest, ofs, Math.min(len - ofs, maxRead));
492 }
493
494
495 private void readFields() {
496 if (bytes == null) {
497 this.kvs = new KeyValue[0];
498 return;
499 }
500 byte [] buf = bytes.get();
501 int offset = bytes.getOffset();
502 int finalOffset = bytes.getSize() + offset;
503 List<KeyValue> kvs = new ArrayList<KeyValue>();
504 while(offset < finalOffset) {
505 int keyLength = Bytes.toInt(buf, offset);
506 offset += Bytes.SIZEOF_INT;
507 kvs.add(new KeyValue(buf, offset, keyLength));
508 offset += keyLength;
509 }
510 this.kvs = kvs.toArray(new KeyValue[kvs.size()]);
511 }
512
513 public long getWritableSize() {
514 if (isEmpty())
515 return Bytes.SIZEOF_INT;
516
517 long size = Bytes.SIZEOF_INT;
518
519 for (KeyValue kv : kvs) {
520 size += kv.getLength();
521 size += Bytes.SIZEOF_INT;
522 }
523
524 return size;
525 }
526
527 public void write(final DataOutput out)
528 throws IOException {
529 if(isEmpty()) {
530 out.writeInt(0);
531 } else {
532 int totalLen = 0;
533 for(KeyValue kv : kvs) {
534 totalLen += kv.getLength() + Bytes.SIZEOF_INT;
535 }
536 out.writeInt(totalLen);
537 for(KeyValue kv : kvs) {
538 out.writeInt(kv.getLength());
539 out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
540 }
541 }
542 }
543
544 public static long getWriteArraySize(Result [] results) {
545 long size = Bytes.SIZEOF_BYTE;
546 if (results == null || results.length == 0) {
547 size += Bytes.SIZEOF_INT;
548 return size;
549 }
550
551 size += Bytes.SIZEOF_INT;
552 size += Bytes.SIZEOF_INT;
553 for (Result result : results) {
554 size += Bytes.SIZEOF_INT;
555 if (result == null || result.isEmpty())
556 continue;
557
558 for (KeyValue kv : result.raw()) {
559 size += Bytes.SIZEOF_INT;
560 size += kv.getLength();
561 }
562 }
563
564 return size;
565 }
566
567 public static void writeArray(final DataOutput out, Result [] results)
568 throws IOException {
569
570
571
572
573 out.writeByte(RESULT_VERSION);
574 if(results == null || results.length == 0) {
575 out.writeInt(0);
576 return;
577 }
578 out.writeInt(results.length);
579 int bufLen = 0;
580 for(Result result : results) {
581 bufLen += Bytes.SIZEOF_INT;
582 if(result == null || result.isEmpty()) {
583 continue;
584 }
585 for(KeyValue key : result.raw()) {
586 bufLen += key.getLength() + Bytes.SIZEOF_INT;
587 }
588 }
589 out.writeInt(bufLen);
590 for(Result result : results) {
591 if(result == null || result.isEmpty()) {
592 out.writeInt(0);
593 continue;
594 }
595 out.writeInt(result.size());
596 for(KeyValue kv : result.raw()) {
597 out.writeInt(kv.getLength());
598 out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
599 }
600 }
601 }
602
603 public static Result [] readArray(final DataInput in)
604 throws IOException {
605
606
607
608
609 int version = in.readByte();
610 if (version > RESULT_VERSION) {
611 throw new IOException("version not supported");
612 }
613 int numResults = in.readInt();
614 if(numResults == 0) {
615 return new Result[0];
616 }
617 Result [] results = new Result[numResults];
618 int bufSize = in.readInt();
619 byte [] buf = new byte[bufSize];
620 int offset = 0;
621 for(int i=0;i<numResults;i++) {
622 int numKeys = in.readInt();
623 offset += Bytes.SIZEOF_INT;
624 if(numKeys == 0) {
625 results[i] = new Result((ImmutableBytesWritable)null);
626 continue;
627 }
628 int initialOffset = offset;
629 for(int j=0;j<numKeys;j++) {
630 int keyLen = in.readInt();
631 Bytes.putInt(buf, offset, keyLen);
632 offset += Bytes.SIZEOF_INT;
633 in.readFully(buf, offset, keyLen);
634 offset += keyLen;
635 }
636 int totalLength = offset - initialOffset;
637 results[i] = new Result(new ImmutableBytesWritable(buf, initialOffset,
638 totalLength));
639 }
640 return results;
641 }
642
643
644
645
646
647
648
649 public static void compareResults(Result res1, Result res2)
650 throws Exception {
651 if (res2 == null) {
652 throw new Exception("There wasn't enough rows, we stopped at "
653 + Bytes.toStringBinary(res1.getRow()));
654 }
655 if (res1.size() != res2.size()) {
656 throw new Exception("This row doesn't have the same number of KVs: "
657 + res1.toString() + " compared to " + res2.toString());
658 }
659 KeyValue[] ourKVs = res1.raw();
660 KeyValue[] replicatedKVs = res2.raw();
661 for (int i = 0; i < res1.size(); i++) {
662 if (!ourKVs[i].equals(replicatedKVs[i]) ||
663 !Bytes.equals(ourKVs[i].getValue(), replicatedKVs[i].getValue())) {
664 throw new Exception("This result was different: "
665 + res1.toString() + " compared to " + res2.toString());
666 }
667 }
668 }
669
670
671
672
673
674 public void copyFrom(Result other) {
675 this.row = other.row;
676 this.bytes = other.bytes;
677 this.familyMap = other.familyMap;
678 this.kvs = other.kvs;
679 }
680 }