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