1 /* 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 package org.apache.hadoop.hbase.client; 21 22 import java.io.IOException; 23 import java.util.ArrayList; 24 import java.util.List; 25 import java.util.Map; 26 27 import org.apache.hadoop.classification.InterfaceAudience; 28 import org.apache.hadoop.classification.InterfaceStability; 29 import org.apache.hadoop.hbase.Cell; 30 import org.apache.hadoop.hbase.CellUtil; 31 import org.apache.hadoop.hbase.HConstants; 32 import org.apache.hadoop.hbase.KeyValue; 33 import org.apache.hadoop.hbase.util.Bytes; 34 35 /** 36 * Used to perform Delete operations on a single row. 37 * <p> 38 * To delete an entire row, instantiate a Delete object with the row 39 * to delete. To further define the scope of what to delete, perform 40 * additional methods as outlined below. 41 * <p> 42 * To delete specific families, execute {@link #deleteFamily(byte[]) deleteFamily} 43 * for each family to delete. 44 * <p> 45 * To delete multiple versions of specific columns, execute 46 * {@link #deleteColumns(byte[], byte[]) deleteColumns} 47 * for each column to delete. 48 * <p> 49 * To delete specific versions of specific columns, execute 50 * {@link #deleteColumn(byte[], byte[], long) deleteColumn} 51 * for each column version to delete. 52 * <p> 53 * Specifying timestamps, deleteFamily and deleteColumns will delete all 54 * versions with a timestamp less than or equal to that passed. If no 55 * timestamp is specified, an entry is added with a timestamp of 'now' 56 * where 'now' is the servers's System.currentTimeMillis(). 57 * Specifying a timestamp to the deleteColumn method will 58 * delete versions only with a timestamp equal to that specified. 59 * If no timestamp is passed to deleteColumn, internally, it figures the 60 * most recent cell's timestamp and adds a delete at that timestamp; i.e. 61 * it deletes the most recently added cell. 62 * <p>The timestamp passed to the constructor is used ONLY for delete of 63 * rows. For anything less -- a deleteColumn, deleteColumns or 64 * deleteFamily -- then you need to use the method overrides that take a 65 * timestamp. The constructor timestamp is not referenced. 66 */ 67 @InterfaceAudience.Public 68 @InterfaceStability.Stable 69 public class Delete extends Mutation implements Comparable<Row> { 70 /** 71 * Create a Delete operation for the specified row. 72 * <p> 73 * If no further operations are done, this will delete everything 74 * associated with the specified row (all versions of all columns in all 75 * families). 76 * @param row row key 77 */ 78 public Delete(byte [] row) { 79 this(row, HConstants.LATEST_TIMESTAMP); 80 } 81 82 /** 83 * Create a Delete operation for the specified row and timestamp.<p> 84 * 85 * If no further operations are done, this will delete all columns in all 86 * families of the specified row with a timestamp less than or equal to the 87 * specified timestamp.<p> 88 * 89 * This timestamp is ONLY used for a delete row operation. If specifying 90 * families or columns, you must specify each timestamp individually. 91 * @param row row key 92 * @param timestamp maximum version timestamp (only for delete row) 93 */ 94 public Delete(byte [] row, long timestamp) { 95 this(row, 0, row.length, timestamp); 96 } 97 98 /** 99 * Create a Delete operation for the specified row and timestamp.<p> 100 * 101 * If no further operations are done, this will delete all columns in all 102 * families of the specified row with a timestamp less than or equal to the 103 * specified timestamp.<p> 104 * 105 * This timestamp is ONLY used for a delete row operation. If specifying 106 * families or columns, you must specify each timestamp individually. 107 * @param rowArray We make a local copy of this passed in row. 108 * @param rowOffset 109 * @param rowLength 110 */ 111 public Delete(final byte [] rowArray, final int rowOffset, final int rowLength) { 112 this(rowArray, rowOffset, rowLength, HConstants.LATEST_TIMESTAMP); 113 } 114 115 /** 116 * Create a Delete operation for the specified row and timestamp.<p> 117 * 118 * If no further operations are done, this will delete all columns in all 119 * families of the specified row with a timestamp less than or equal to the 120 * specified timestamp.<p> 121 * 122 * This timestamp is ONLY used for a delete row operation. If specifying 123 * families or columns, you must specify each timestamp individually. 124 * @param rowArray We make a local copy of this passed in row. 125 * @param rowOffset 126 * @param rowLength 127 * @param ts maximum version timestamp (only for delete row) 128 */ 129 public Delete(final byte [] rowArray, final int rowOffset, final int rowLength, long ts) { 130 checkRow(rowArray, rowOffset, rowLength); 131 this.row = Bytes.copy(rowArray, rowOffset, rowLength); 132 setTimestamp(ts); 133 } 134 135 /** 136 * @param d Delete to clone. 137 */ 138 public Delete(final Delete d) { 139 this.row = d.getRow(); 140 this.ts = d.getTimeStamp(); 141 this.familyMap.putAll(d.getFamilyCellMap()); 142 this.durability = d.durability; 143 } 144 145 /** 146 * Advanced use only. 147 * Add an existing delete marker to this Delete object. 148 * @param kv An existing KeyValue of type "delete". 149 * @return this for invocation chaining 150 * @throws IOException 151 */ 152 @SuppressWarnings("unchecked") 153 public Delete addDeleteMarker(Cell kv) throws IOException { 154 // TODO: Deprecate and rename 'add' so it matches how we add KVs to Puts. 155 if (!CellUtil.isDelete(kv)) { 156 throw new IOException("The recently added KeyValue is not of type " 157 + "delete. Rowkey: " + Bytes.toStringBinary(this.row)); 158 } 159 if (Bytes.compareTo(this.row, 0, row.length, kv.getRowArray(), 160 kv.getRowOffset(), kv.getRowLength()) != 0) { 161 throw new WrongRowIOException("The row in " + kv.toString() + 162 " doesn't match the original one " + Bytes.toStringBinary(this.row)); 163 } 164 byte [] family = CellUtil.cloneFamily(kv); 165 List<Cell> list = familyMap.get(family); 166 if (list == null) { 167 list = new ArrayList<Cell>(); 168 } 169 list.add(kv); 170 familyMap.put(family, list); 171 return this; 172 } 173 174 /** 175 * Delete all versions of all columns of the specified family. 176 * <p> 177 * Overrides previous calls to deleteColumn and deleteColumns for the 178 * specified family. 179 * @param family family name 180 * @return this for invocation chaining 181 */ 182 public Delete deleteFamily(byte [] family) { 183 this.deleteFamily(family, this.ts); 184 return this; 185 } 186 187 /** 188 * Delete all columns of the specified family with a timestamp less than 189 * or equal to the specified timestamp. 190 * <p> 191 * Overrides previous calls to deleteColumn and deleteColumns for the 192 * specified family. 193 * @param family family name 194 * @param timestamp maximum version timestamp 195 * @return this for invocation chaining 196 */ 197 @SuppressWarnings("unchecked") 198 public Delete deleteFamily(byte [] family, long timestamp) { 199 if (timestamp < 0) { 200 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp); 201 } 202 List<Cell> list = familyMap.get(family); 203 if(list == null) { 204 list = new ArrayList<Cell>(); 205 } else if(!list.isEmpty()) { 206 list.clear(); 207 } 208 KeyValue kv = new KeyValue(row, family, null, timestamp, KeyValue.Type.DeleteFamily); 209 list.add(kv); 210 familyMap.put(family, list); 211 return this; 212 } 213 214 /** 215 * Delete all columns of the specified family with a timestamp equal to 216 * the specified timestamp. 217 * @param family family name 218 * @param timestamp version timestamp 219 * @return this for invocation chaining 220 */ 221 public Delete deleteFamilyVersion(byte [] family, long timestamp) { 222 List<Cell> list = familyMap.get(family); 223 if(list == null) { 224 list = new ArrayList<Cell>(); 225 } 226 list.add(new KeyValue(row, family, null, timestamp, 227 KeyValue.Type.DeleteFamilyVersion)); 228 familyMap.put(family, list); 229 return this; 230 } 231 232 233 /** 234 * Delete all versions of the specified column. 235 * @param family family name 236 * @param qualifier column qualifier 237 * @return this for invocation chaining 238 */ 239 public Delete deleteColumns(byte [] family, byte [] qualifier) { 240 this.deleteColumns(family, qualifier, this.ts); 241 return this; 242 } 243 244 /** 245 * Delete all versions of the specified column with a timestamp less than 246 * or equal to the specified timestamp. 247 * @param family family name 248 * @param qualifier column qualifier 249 * @param timestamp maximum version timestamp 250 * @return this for invocation chaining 251 */ 252 @SuppressWarnings("unchecked") 253 public Delete deleteColumns(byte [] family, byte [] qualifier, long timestamp) { 254 if (timestamp < 0) { 255 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp); 256 } 257 List<Cell> list = familyMap.get(family); 258 if (list == null) { 259 list = new ArrayList<Cell>(); 260 } 261 list.add(new KeyValue(this.row, family, qualifier, timestamp, 262 KeyValue.Type.DeleteColumn)); 263 familyMap.put(family, list); 264 return this; 265 } 266 267 /** 268 * Delete the latest version of the specified column. 269 * This is an expensive call in that on the server-side, it first does a 270 * get to find the latest versions timestamp. Then it adds a delete using 271 * the fetched cells timestamp. 272 * @param family family name 273 * @param qualifier column qualifier 274 * @return this for invocation chaining 275 */ 276 public Delete deleteColumn(byte [] family, byte [] qualifier) { 277 this.deleteColumn(family, qualifier, this.ts); 278 return this; 279 } 280 281 /** 282 * Delete the specified version of the specified column. 283 * @param family family name 284 * @param qualifier column qualifier 285 * @param timestamp version timestamp 286 * @return this for invocation chaining 287 */ 288 @SuppressWarnings("unchecked") 289 public Delete deleteColumn(byte [] family, byte [] qualifier, long timestamp) { 290 if (timestamp < 0) { 291 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp); 292 } 293 List<Cell> list = familyMap.get(family); 294 if(list == null) { 295 list = new ArrayList<Cell>(); 296 } 297 KeyValue kv = new KeyValue(this.row, family, qualifier, timestamp, KeyValue.Type.Delete); 298 list.add(kv); 299 familyMap.put(family, list); 300 return this; 301 } 302 303 /** 304 * Set the timestamp of the delete. 305 * 306 * @param timestamp 307 */ 308 public void setTimestamp(long timestamp) { 309 if (timestamp < 0) { 310 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp); 311 } 312 this.ts = timestamp; 313 } 314 315 @Override 316 public Map<String, Object> toMap(int maxCols) { 317 // we start with the fingerprint map and build on top of it. 318 Map<String, Object> map = super.toMap(maxCols); 319 // why is put not doing this? 320 map.put("ts", this.ts); 321 return map; 322 } 323 }