001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.client; 020 021import java.io.IOException; 022import java.nio.ByteBuffer; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029import java.util.NavigableMap; 030import java.util.Optional; 031import java.util.TreeMap; 032import java.util.UUID; 033import java.util.stream.Collectors; 034import org.apache.hadoop.hbase.Cell; 035import org.apache.hadoop.hbase.CellScannable; 036import org.apache.hadoop.hbase.CellScanner; 037import org.apache.hadoop.hbase.CellUtil; 038import org.apache.hadoop.hbase.ExtendedCell; 039import org.apache.hadoop.hbase.HConstants; 040import org.apache.hadoop.hbase.IndividualBytesFieldCell; 041import org.apache.hadoop.hbase.KeyValue; 042import org.apache.hadoop.hbase.PrivateCellUtil; 043import org.apache.hadoop.hbase.RawCell; 044import org.apache.hadoop.hbase.Tag; 045import org.apache.hadoop.hbase.exceptions.DeserializationException; 046import org.apache.hadoop.hbase.io.HeapSize; 047import org.apache.hadoop.hbase.protobuf.ProtobufUtil; 048import org.apache.hadoop.hbase.protobuf.generated.ClientProtos; 049import org.apache.hadoop.hbase.security.access.AccessControlConstants; 050import org.apache.hadoop.hbase.security.access.AccessControlUtil; 051import org.apache.hadoop.hbase.security.access.Permission; 052import org.apache.hadoop.hbase.security.visibility.CellVisibility; 053import org.apache.hadoop.hbase.security.visibility.VisibilityConstants; 054import org.apache.hadoop.hbase.util.Bytes; 055import org.apache.hadoop.hbase.util.ClassSize; 056import org.apache.yetus.audience.InterfaceAudience; 057 058import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 059import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap; 060import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap; 061import org.apache.hbase.thirdparty.com.google.common.io.ByteArrayDataInput; 062import org.apache.hbase.thirdparty.com.google.common.io.ByteArrayDataOutput; 063import org.apache.hbase.thirdparty.com.google.common.io.ByteStreams; 064 065@InterfaceAudience.Public 066public abstract class Mutation extends OperationWithAttributes implements Row, CellScannable, 067 HeapSize { 068 public static final long MUTATION_OVERHEAD = ClassSize.align( 069 // This 070 ClassSize.OBJECT + 071 // row + OperationWithAttributes.attributes 072 2 * ClassSize.REFERENCE + 073 // Timestamp 074 1 * Bytes.SIZEOF_LONG + 075 // durability 076 ClassSize.REFERENCE + 077 // familyMap 078 ClassSize.REFERENCE + 079 // familyMap 080 ClassSize.TREEMAP + 081 // priority 082 ClassSize.INTEGER 083 ); 084 085 /** 086 * The attribute for storing the list of clusters that have consumed the change. 087 */ 088 private static final String CONSUMED_CLUSTER_IDS = "_cs.id"; 089 090 /** 091 * The attribute for storing TTL for the result of the mutation. 092 */ 093 private static final String OP_ATTRIBUTE_TTL = "_ttl"; 094 095 private static final String RETURN_RESULTS = "_rr_"; 096 097 // TODO: row should be final 098 protected byte [] row = null; 099 protected long ts = HConstants.LATEST_TIMESTAMP; 100 protected Durability durability = Durability.USE_DEFAULT; 101 102 // TODO: familyMap should be final 103 // A Map sorted by column family. 104 protected NavigableMap<byte [], List<Cell>> familyMap; 105 106 /** 107 * empty construction. 108 * We need this empty construction to keep binary compatibility. 109 */ 110 protected Mutation() { 111 this.familyMap = new TreeMap<>(Bytes.BYTES_COMPARATOR); 112 } 113 114 protected Mutation(Mutation clone) { 115 super(clone); 116 this.row = clone.getRow(); 117 this.ts = clone.getTimestamp(); 118 this.familyMap = clone.getFamilyCellMap().entrySet().stream(). 119 collect(Collectors.toMap(e -> e.getKey(), e -> new ArrayList<>(e.getValue()), (k, v) -> { 120 throw new RuntimeException("collisions!!!"); 121 }, () -> new TreeMap<>(Bytes.BYTES_COMPARATOR))); 122 } 123 124 /** 125 * Construct the mutation with user defined data. 126 * @param row row. CAN'T be null 127 * @param ts timestamp 128 * @param familyMap the map to collect all cells internally. CAN'T be null 129 */ 130 protected Mutation(byte[] row, long ts, NavigableMap<byte [], List<Cell>> familyMap) { 131 this.row = Preconditions.checkNotNull(row); 132 if (row.length == 0) { 133 throw new IllegalArgumentException("Row can't be empty"); 134 } 135 this.ts = ts; 136 this.familyMap = Preconditions.checkNotNull(familyMap); 137 } 138 139 @Override 140 public CellScanner cellScanner() { 141 return CellUtil.createCellScanner(getFamilyCellMap()); 142 } 143 144 /** 145 * Creates an empty list if one doesn't exist for the given column family 146 * or else it returns the associated list of Cell objects. 147 * 148 * @param family column family 149 * @return a list of Cell objects, returns an empty list if one doesn't exist. 150 */ 151 List<Cell> getCellList(byte[] family) { 152 List<Cell> list = this.familyMap.get(family); 153 if (list == null) { 154 list = new ArrayList<>(); 155 this.familyMap.put(family, list); 156 } 157 return list; 158 } 159 160 /* 161 * Create a KeyValue with this objects row key and the Put identifier. 162 * 163 * @return a KeyValue with this objects row key and the Put identifier. 164 */ 165 KeyValue createPutKeyValue(byte[] family, byte[] qualifier, long ts, byte[] value) { 166 return new KeyValue(this.row, family, qualifier, ts, KeyValue.Type.Put, value); 167 } 168 169 /** 170 * Create a KeyValue with this objects row key and the Put identifier. 171 * @param family 172 * @param qualifier 173 * @param ts 174 * @param value 175 * @param tags - Specify the Tags as an Array 176 * @return a KeyValue with this objects row key and the Put identifier. 177 */ 178 KeyValue createPutKeyValue(byte[] family, byte[] qualifier, long ts, byte[] value, Tag[] tags) { 179 KeyValue kvWithTag = new KeyValue(this.row, family, qualifier, ts, value, tags); 180 return kvWithTag; 181 } 182 183 /* 184 * Create a KeyValue with this objects row key and the Put identifier. 185 * 186 * @return a KeyValue with this objects row key and the Put identifier. 187 */ 188 KeyValue createPutKeyValue(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value, 189 Tag[] tags) { 190 return new KeyValue(this.row, 0, this.row == null ? 0 : this.row.length, 191 family, 0, family == null ? 0 : family.length, 192 qualifier, ts, KeyValue.Type.Put, value, tags != null ? Arrays.asList(tags) : null); 193 } 194 195 /** 196 * Compile the column family (i.e. schema) information 197 * into a Map. Useful for parsing and aggregation by debugging, 198 * logging, and administration tools. 199 * @return Map 200 */ 201 @Override 202 public Map<String, Object> getFingerprint() { 203 Map<String, Object> map = new HashMap<>(); 204 List<String> families = new ArrayList<>(this.familyMap.entrySet().size()); 205 // ideally, we would also include table information, but that information 206 // is not stored in each Operation instance. 207 map.put("families", families); 208 for (Map.Entry<byte [], List<Cell>> entry : this.familyMap.entrySet()) { 209 families.add(Bytes.toStringBinary(entry.getKey())); 210 } 211 return map; 212 } 213 214 /** 215 * Compile the details beyond the scope of getFingerprint (row, columns, 216 * timestamps, etc.) into a Map along with the fingerprinted information. 217 * Useful for debugging, logging, and administration tools. 218 * @param maxCols a limit on the number of columns output prior to truncation 219 * @return Map 220 */ 221 @Override 222 public Map<String, Object> toMap(int maxCols) { 223 // we start with the fingerprint map and build on top of it. 224 Map<String, Object> map = getFingerprint(); 225 // replace the fingerprint's simple list of families with a 226 // map from column families to lists of qualifiers and kv details 227 Map<String, List<Map<String, Object>>> columns = new HashMap<>(); 228 map.put("families", columns); 229 map.put("row", Bytes.toStringBinary(this.row)); 230 int colCount = 0; 231 // iterate through all column families affected 232 for (Map.Entry<byte [], List<Cell>> entry : this.familyMap.entrySet()) { 233 // map from this family to details for each cell affected within the family 234 List<Map<String, Object>> qualifierDetails = new ArrayList<>(); 235 columns.put(Bytes.toStringBinary(entry.getKey()), qualifierDetails); 236 colCount += entry.getValue().size(); 237 if (maxCols <= 0) { 238 continue; 239 } 240 // add details for each cell 241 for (Cell cell: entry.getValue()) { 242 if (--maxCols <= 0) { 243 continue; 244 } 245 Map<String, Object> cellMap = cellToStringMap(cell); 246 qualifierDetails.add(cellMap); 247 } 248 } 249 map.put("totalColumns", colCount); 250 // add the id if set 251 if (getId() != null) { 252 map.put("id", getId()); 253 } 254 // Add the TTL if set 255 // Long.MAX_VALUE is the default, and is interpreted to mean this attribute 256 // has not been set. 257 if (getTTL() != Long.MAX_VALUE) { 258 map.put("ttl", getTTL()); 259 } 260 map.put("ts", this.ts); 261 return map; 262 } 263 264 private static Map<String, Object> cellToStringMap(Cell c) { 265 Map<String, Object> stringMap = new HashMap<>(); 266 stringMap.put("qualifier", Bytes.toStringBinary(c.getQualifierArray(), c.getQualifierOffset(), 267 c.getQualifierLength())); 268 stringMap.put("timestamp", c.getTimestamp()); 269 stringMap.put("vlen", c.getValueLength()); 270 List<Tag> tags = PrivateCellUtil.getTags(c); 271 if (tags != null) { 272 List<String> tagsString = new ArrayList<>(tags.size()); 273 for (Tag t : tags) { 274 tagsString 275 .add((t.getType()) + ":" + Bytes.toStringBinary(Tag.cloneValue(t))); 276 } 277 stringMap.put("tag", tagsString); 278 } 279 return stringMap; 280 } 281 282 /** 283 * Set the durability for this mutation 284 * @param d 285 */ 286 public Mutation setDurability(Durability d) { 287 this.durability = d; 288 return this; 289 } 290 291 /** Get the current durability */ 292 public Durability getDurability() { 293 return this.durability; 294 } 295 296 /** 297 * Method for retrieving the put's familyMap 298 * @return familyMap 299 */ 300 public NavigableMap<byte [], List<Cell>> getFamilyCellMap() { 301 return this.familyMap; 302 } 303 304 /** 305 * Method for setting the mutation's familyMap 306 * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0. 307 * Use {@link Mutation#Mutation(byte[], long, NavigableMap)} instead 308 */ 309 @Deprecated 310 public Mutation setFamilyCellMap(NavigableMap<byte [], List<Cell>> map) { 311 // TODO: Shut this down or move it up to be a Constructor. Get new object rather than change 312 // this internal data member. 313 this.familyMap = map; 314 return this; 315 } 316 317 /** 318 * Method to check if the familyMap is empty 319 * @return true if empty, false otherwise 320 */ 321 public boolean isEmpty() { 322 return familyMap.isEmpty(); 323 } 324 325 /** 326 * Method for retrieving the delete's row 327 * @return row 328 */ 329 @Override 330 public byte [] getRow() { 331 return this.row; 332 } 333 334 /** 335 * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0. 336 * Use {@link Row#COMPARATOR} instead 337 */ 338 @Deprecated 339 @Override 340 public int compareTo(final Row d) { 341 return Bytes.compareTo(this.getRow(), d.getRow()); 342 } 343 344 /** 345 * Method for retrieving the timestamp 346 * @return timestamp 347 * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0. 348 * Use {@link #getTimestamp()} instead 349 */ 350 @Deprecated 351 public long getTimeStamp() { 352 return this.getTimestamp(); 353 } 354 355 /** 356 * Method for retrieving the timestamp. 357 * 358 * @return timestamp 359 */ 360 public long getTimestamp() { 361 return this.ts; 362 } 363 364 /** 365 * Marks that the clusters with the given clusterIds have consumed the mutation 366 * @param clusterIds of the clusters that have consumed the mutation 367 */ 368 public Mutation setClusterIds(List<UUID> clusterIds) { 369 ByteArrayDataOutput out = ByteStreams.newDataOutput(); 370 out.writeInt(clusterIds.size()); 371 for (UUID clusterId : clusterIds) { 372 out.writeLong(clusterId.getMostSignificantBits()); 373 out.writeLong(clusterId.getLeastSignificantBits()); 374 } 375 setAttribute(CONSUMED_CLUSTER_IDS, out.toByteArray()); 376 return this; 377 } 378 379 /** 380 * @return the set of clusterIds that have consumed the mutation 381 */ 382 public List<UUID> getClusterIds() { 383 List<UUID> clusterIds = new ArrayList<>(); 384 byte[] bytes = getAttribute(CONSUMED_CLUSTER_IDS); 385 if(bytes != null) { 386 ByteArrayDataInput in = ByteStreams.newDataInput(bytes); 387 int numClusters = in.readInt(); 388 for(int i=0; i<numClusters; i++){ 389 clusterIds.add(new UUID(in.readLong(), in.readLong())); 390 } 391 } 392 return clusterIds; 393 } 394 395 /** 396 * Sets the visibility expression associated with cells in this Mutation. 397 * @param expression 398 */ 399 public Mutation setCellVisibility(CellVisibility expression) { 400 this.setAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY, 401 toCellVisibility(expression).toByteArray()); 402 return this; 403 } 404 405 /** 406 * @return CellVisibility associated with cells in this Mutation. 407 * @throws DeserializationException 408 */ 409 public CellVisibility getCellVisibility() throws DeserializationException { 410 byte[] cellVisibilityBytes = this.getAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY); 411 if (cellVisibilityBytes == null) return null; 412 return toCellVisibility(cellVisibilityBytes); 413 } 414 415 /** 416 * Create a protocol buffer CellVisibility based on a client CellVisibility. 417 * 418 * @param cellVisibility 419 * @return a protocol buffer CellVisibility 420 */ 421 static ClientProtos.CellVisibility toCellVisibility(CellVisibility cellVisibility) { 422 ClientProtos.CellVisibility.Builder builder = ClientProtos.CellVisibility.newBuilder(); 423 builder.setExpression(cellVisibility.getExpression()); 424 return builder.build(); 425 } 426 427 /** 428 * Convert a protocol buffer CellVisibility to a client CellVisibility 429 * 430 * @param proto 431 * @return the converted client CellVisibility 432 */ 433 private static CellVisibility toCellVisibility(ClientProtos.CellVisibility proto) { 434 if (proto == null) return null; 435 return new CellVisibility(proto.getExpression()); 436 } 437 438 /** 439 * Convert a protocol buffer CellVisibility bytes to a client CellVisibility 440 * 441 * @param protoBytes 442 * @return the converted client CellVisibility 443 * @throws DeserializationException 444 */ 445 private static CellVisibility toCellVisibility(byte[] protoBytes) throws DeserializationException { 446 if (protoBytes == null) return null; 447 ClientProtos.CellVisibility.Builder builder = ClientProtos.CellVisibility.newBuilder(); 448 ClientProtos.CellVisibility proto = null; 449 try { 450 ProtobufUtil.mergeFrom(builder, protoBytes); 451 proto = builder.build(); 452 } catch (IOException e) { 453 throw new DeserializationException(e); 454 } 455 return toCellVisibility(proto); 456 } 457 458 /** 459 * Number of KeyValues carried by this Mutation. 460 * @return the total number of KeyValues 461 */ 462 public int size() { 463 int size = 0; 464 for (List<Cell> cells : this.familyMap.values()) { 465 size += cells.size(); 466 } 467 return size; 468 } 469 470 /** 471 * @return the number of different families 472 */ 473 public int numFamilies() { 474 return familyMap.size(); 475 } 476 477 /** 478 * @return Calculate what Mutation adds to class heap size. 479 */ 480 @Override 481 public long heapSize() { 482 long heapsize = MUTATION_OVERHEAD; 483 // Adding row 484 heapsize += ClassSize.align(ClassSize.ARRAY + this.row.length); 485 486 // Adding map overhead 487 heapsize += 488 ClassSize.align(this.familyMap.size() * ClassSize.MAP_ENTRY); 489 for(Map.Entry<byte [], List<Cell>> entry : this.familyMap.entrySet()) { 490 //Adding key overhead 491 heapsize += 492 ClassSize.align(ClassSize.ARRAY + entry.getKey().length); 493 494 //This part is kinds tricky since the JVM can reuse references if you 495 //store the same value, but have a good match with SizeOf at the moment 496 //Adding value overhead 497 heapsize += ClassSize.align(ClassSize.ARRAYLIST); 498 int size = entry.getValue().size(); 499 heapsize += ClassSize.align(ClassSize.ARRAY + 500 size * ClassSize.REFERENCE); 501 502 for(Cell cell : entry.getValue()) { 503 heapsize += PrivateCellUtil.estimatedSizeOfCell(cell); 504 } 505 } 506 heapsize += getAttributeSize(); 507 heapsize += extraHeapSize(); 508 return ClassSize.align(heapsize); 509 } 510 511 /** 512 * @return The serialized ACL for this operation, or null if none 513 */ 514 public byte[] getACL() { 515 return getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL); 516 } 517 518 /** 519 * @param user User short name 520 * @param perms Permissions for the user 521 */ 522 public Mutation setACL(String user, Permission perms) { 523 setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL, 524 AccessControlUtil.toUsersAndPermissions(user, perms).toByteArray()); 525 return this; 526 } 527 528 /** 529 * @param perms A map of permissions for a user or users 530 */ 531 public Mutation setACL(Map<String, Permission> perms) { 532 ListMultimap<String, Permission> permMap = ArrayListMultimap.create(); 533 for (Map.Entry<String, Permission> entry : perms.entrySet()) { 534 permMap.put(entry.getKey(), entry.getValue()); 535 } 536 setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL, 537 AccessControlUtil.toUsersAndPermissions(permMap).toByteArray()); 538 return this; 539 } 540 541 /** 542 * Return the TTL requested for the result of the mutation, in milliseconds. 543 * @return the TTL requested for the result of the mutation, in milliseconds, 544 * or Long.MAX_VALUE if unset 545 */ 546 public long getTTL() { 547 byte[] ttlBytes = getAttribute(OP_ATTRIBUTE_TTL); 548 if (ttlBytes != null) { 549 return Bytes.toLong(ttlBytes); 550 } 551 return Long.MAX_VALUE; 552 } 553 554 /** 555 * Set the TTL desired for the result of the mutation, in milliseconds. 556 * @param ttl the TTL desired for the result of the mutation, in milliseconds 557 * @return this 558 */ 559 public Mutation setTTL(long ttl) { 560 setAttribute(OP_ATTRIBUTE_TTL, Bytes.toBytes(ttl)); 561 return this; 562 } 563 564 /** 565 * @return current value for returnResults 566 */ 567 // Used by Increment and Append only. 568 @InterfaceAudience.Private 569 protected boolean isReturnResults() { 570 byte[] v = getAttribute(RETURN_RESULTS); 571 return v == null ? true : Bytes.toBoolean(v); 572 } 573 574 @InterfaceAudience.Private 575 // Used by Increment and Append only. 576 protected Mutation setReturnResults(boolean returnResults) { 577 setAttribute(RETURN_RESULTS, Bytes.toBytes(returnResults)); 578 return this; 579 } 580 581 /** 582 * Subclasses should override this method to add the heap size of their own fields. 583 * @return the heap size to add (will be aligned). 584 */ 585 protected long extraHeapSize(){ 586 return 0L; 587 } 588 589 /** 590 * Set the timestamp of the delete. 591 */ 592 public Mutation setTimestamp(long timestamp) { 593 if (timestamp < 0) { 594 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp); 595 } 596 this.ts = timestamp; 597 return this; 598 } 599 600 /** 601 * A convenience method to determine if this object's familyMap contains 602 * a value assigned to the given family & qualifier. 603 * Both given arguments must match the KeyValue object to return true. 604 * 605 * @param family column family 606 * @param qualifier column qualifier 607 * @return returns true if the given family and qualifier already has an 608 * existing KeyValue object in the family map. 609 */ 610 public boolean has(byte [] family, byte [] qualifier) { 611 return has(family, qualifier, this.ts, HConstants.EMPTY_BYTE_ARRAY, true, true); 612 } 613 614 /** 615 * A convenience method to determine if this object's familyMap contains 616 * a value assigned to the given family, qualifier and timestamp. 617 * All 3 given arguments must match the KeyValue object to return true. 618 * 619 * @param family column family 620 * @param qualifier column qualifier 621 * @param ts timestamp 622 * @return returns true if the given family, qualifier and timestamp already has an 623 * existing KeyValue object in the family map. 624 */ 625 public boolean has(byte [] family, byte [] qualifier, long ts) { 626 return has(family, qualifier, ts, HConstants.EMPTY_BYTE_ARRAY, false, true); 627 } 628 629 /** 630 * A convenience method to determine if this object's familyMap contains 631 * a value assigned to the given family, qualifier and timestamp. 632 * All 3 given arguments must match the KeyValue object to return true. 633 * 634 * @param family column family 635 * @param qualifier column qualifier 636 * @param value value to check 637 * @return returns true if the given family, qualifier and value already has an 638 * existing KeyValue object in the family map. 639 */ 640 public boolean has(byte [] family, byte [] qualifier, byte [] value) { 641 return has(family, qualifier, this.ts, value, true, false); 642 } 643 644 /** 645 * A convenience method to determine if this object's familyMap contains 646 * the given value assigned to the given family, qualifier and timestamp. 647 * All 4 given arguments must match the KeyValue object to return true. 648 * 649 * @param family column family 650 * @param qualifier column qualifier 651 * @param ts timestamp 652 * @param value value to check 653 * @return returns true if the given family, qualifier timestamp and value 654 * already has an existing KeyValue object in the family map. 655 */ 656 public boolean has(byte [] family, byte [] qualifier, long ts, byte [] value) { 657 return has(family, qualifier, ts, value, false, false); 658 } 659 660 /** 661 * Returns a list of all KeyValue objects with matching column family and qualifier. 662 * 663 * @param family column family 664 * @param qualifier column qualifier 665 * @return a list of KeyValue objects with the matching family and qualifier, 666 * returns an empty list if one doesn't exist for the given family. 667 */ 668 public List<Cell> get(byte[] family, byte[] qualifier) { 669 List<Cell> filteredList = new ArrayList<>(); 670 for (Cell cell: getCellList(family)) { 671 if (CellUtil.matchingQualifier(cell, qualifier)) { 672 filteredList.add(cell); 673 } 674 } 675 return filteredList; 676 } 677 678 /* 679 * Private method to determine if this object's familyMap contains 680 * the given value assigned to the given family, qualifier and timestamp 681 * respecting the 2 boolean arguments 682 * 683 * @param family 684 * @param qualifier 685 * @param ts 686 * @param value 687 * @param ignoreTS 688 * @param ignoreValue 689 * @return returns true if the given family, qualifier timestamp and value 690 * already has an existing KeyValue object in the family map. 691 */ 692 protected boolean has(byte[] family, byte[] qualifier, long ts, byte[] value, 693 boolean ignoreTS, boolean ignoreValue) { 694 List<Cell> list = getCellList(family); 695 if (list.isEmpty()) { 696 return false; 697 } 698 // Boolean analysis of ignoreTS/ignoreValue. 699 // T T => 2 700 // T F => 3 (first is always true) 701 // F T => 2 702 // F F => 1 703 if (!ignoreTS && !ignoreValue) { 704 for (Cell cell : list) { 705 if (CellUtil.matchingFamily(cell, family) && 706 CellUtil.matchingQualifier(cell, qualifier) && 707 CellUtil.matchingValue(cell, value) && 708 cell.getTimestamp() == ts) { 709 return true; 710 } 711 } 712 } else if (ignoreValue && !ignoreTS) { 713 for (Cell cell : list) { 714 if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier) 715 && cell.getTimestamp() == ts) { 716 return true; 717 } 718 } 719 } else if (!ignoreValue && ignoreTS) { 720 for (Cell cell : list) { 721 if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier) 722 && CellUtil.matchingValue(cell, value)) { 723 return true; 724 } 725 } 726 } else { 727 for (Cell cell : list) { 728 if (CellUtil.matchingFamily(cell, family) && 729 CellUtil.matchingQualifier(cell, qualifier)) { 730 return true; 731 } 732 } 733 } 734 return false; 735 } 736 737 /** 738 * @param row Row to check 739 * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or 740 * > {@link HConstants#MAX_ROW_LENGTH} 741 * @return <code>row</code> 742 */ 743 static byte [] checkRow(final byte [] row) { 744 return checkRow(row, 0, row == null? 0: row.length); 745 } 746 747 /** 748 * @param row Row to check 749 * @param offset 750 * @param length 751 * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or 752 * > {@link HConstants#MAX_ROW_LENGTH} 753 * @return <code>row</code> 754 */ 755 static byte [] checkRow(final byte [] row, final int offset, final int length) { 756 if (row == null) { 757 throw new IllegalArgumentException("Row buffer is null"); 758 } 759 if (length == 0) { 760 throw new IllegalArgumentException("Row length is 0"); 761 } 762 if (length > HConstants.MAX_ROW_LENGTH) { 763 throw new IllegalArgumentException("Row length " + length + " is > " + 764 HConstants.MAX_ROW_LENGTH); 765 } 766 return row; 767 } 768 769 static void checkRow(ByteBuffer row) { 770 if (row == null) { 771 throw new IllegalArgumentException("Row buffer is null"); 772 } 773 if (row.remaining() == 0) { 774 throw new IllegalArgumentException("Row length is 0"); 775 } 776 if (row.remaining() > HConstants.MAX_ROW_LENGTH) { 777 throw new IllegalArgumentException("Row length " + row.remaining() + " is > " + 778 HConstants.MAX_ROW_LENGTH); 779 } 780 } 781 782 Mutation add(Cell cell) throws IOException { 783 //Checking that the row of the kv is the same as the mutation 784 // TODO: It is fraught with risk if user pass the wrong row. 785 // Throwing the IllegalArgumentException is more suitable I'd say. 786 if (!CellUtil.matchingRows(cell, this.row)) { 787 throw new WrongRowIOException("The row in " + cell.toString() + 788 " doesn't match the original one " + Bytes.toStringBinary(this.row)); 789 } 790 791 byte[] family; 792 793 if (cell instanceof IndividualBytesFieldCell) { 794 family = cell.getFamilyArray(); 795 } else { 796 family = CellUtil.cloneFamily(cell); 797 } 798 799 if (family == null || family.length == 0) { 800 throw new IllegalArgumentException("Family cannot be null"); 801 } 802 803 if (cell instanceof ExtendedCell) { 804 getCellList(family).add(cell); 805 } else { 806 getCellList(family).add(new CellWrapper(cell)); 807 } 808 return this; 809 } 810 811 private static final class CellWrapper implements ExtendedCell { 812 private static final long FIXED_OVERHEAD = ClassSize.align( 813 ClassSize.OBJECT // object header 814 + KeyValue.TIMESTAMP_SIZE // timestamp 815 + Bytes.SIZEOF_LONG // sequence id 816 + 1 * ClassSize.REFERENCE); // references to cell 817 private final Cell cell; 818 private long sequenceId; 819 private long timestamp; 820 821 CellWrapper(Cell cell) { 822 assert !(cell instanceof ExtendedCell); 823 this.cell = cell; 824 this.sequenceId = cell.getSequenceId(); 825 this.timestamp = cell.getTimestamp(); 826 } 827 828 @Override 829 public void setSequenceId(long seqId) { 830 sequenceId = seqId; 831 } 832 833 @Override 834 public void setTimestamp(long ts) { 835 timestamp = ts; 836 } 837 838 @Override 839 public void setTimestamp(byte[] ts) { 840 timestamp = Bytes.toLong(ts); 841 } 842 843 @Override 844 public long getSequenceId() { 845 return sequenceId; 846 } 847 848 @Override 849 public byte[] getValueArray() { 850 return cell.getValueArray(); 851 } 852 853 @Override 854 public int getValueOffset() { 855 return cell.getValueOffset(); 856 } 857 858 @Override 859 public int getValueLength() { 860 return cell.getValueLength(); 861 } 862 863 @Override 864 public byte[] getTagsArray() { 865 return cell.getTagsArray(); 866 } 867 868 @Override 869 public int getTagsOffset() { 870 return cell.getTagsOffset(); 871 } 872 873 @Override 874 public int getTagsLength() { 875 return cell.getTagsLength(); 876 } 877 878 @Override 879 public byte[] getRowArray() { 880 return cell.getRowArray(); 881 } 882 883 @Override 884 public int getRowOffset() { 885 return cell.getRowOffset(); 886 } 887 888 @Override 889 public short getRowLength() { 890 return cell.getRowLength(); 891 } 892 893 @Override 894 public byte[] getFamilyArray() { 895 return cell.getFamilyArray(); 896 } 897 898 @Override 899 public int getFamilyOffset() { 900 return cell.getFamilyOffset(); 901 } 902 903 @Override 904 public byte getFamilyLength() { 905 return cell.getFamilyLength(); 906 } 907 908 @Override 909 public byte[] getQualifierArray() { 910 return cell.getQualifierArray(); 911 } 912 913 @Override 914 public int getQualifierOffset() { 915 return cell.getQualifierOffset(); 916 } 917 918 @Override 919 public int getQualifierLength() { 920 return cell.getQualifierLength(); 921 } 922 923 @Override 924 public long getTimestamp() { 925 return timestamp; 926 } 927 928 @Override 929 public byte getTypeByte() { 930 return cell.getTypeByte(); 931 } 932 933 @Override 934 public Optional<Tag> getTag(byte type) { 935 if (cell instanceof RawCell) { 936 return ((RawCell) cell).getTag(type); 937 } 938 return PrivateCellUtil.getTag(cell, type); 939 } 940 941 @Override 942 public Iterator<Tag> getTags() { 943 if (cell instanceof RawCell) { 944 return ((RawCell) cell).getTags(); 945 } 946 return PrivateCellUtil.tagsIterator(cell); 947 } 948 949 @Override 950 public byte[] cloneTags() { 951 if (cell instanceof RawCell) { 952 return ((RawCell) cell).cloneTags(); 953 } 954 return PrivateCellUtil.cloneTags(cell); 955 } 956 957 private long heapOverhead() { 958 return FIXED_OVERHEAD 959 + ClassSize.ARRAY // row 960 + getFamilyLength() == 0 ? 0 : ClassSize.ARRAY 961 + getQualifierLength() == 0 ? 0 : ClassSize.ARRAY 962 + getValueLength() == 0 ? 0 : ClassSize.ARRAY 963 + getTagsLength() == 0 ? 0 : ClassSize.ARRAY; 964 } 965 966 @Override 967 public long heapSize() { 968 return heapOverhead() 969 + ClassSize.align(getRowLength()) 970 + ClassSize.align(getFamilyLength()) 971 + ClassSize.align(getQualifierLength()) 972 + ClassSize.align(getValueLength()) 973 + ClassSize.align(getTagsLength()); 974 } 975 } 976}