1 /* 2 * Copyright 2010 The Apache Software Foundation 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package org.apache.hadoop.hbase.client; 22 23 import org.apache.hadoop.hbase.HConstants; 24 import org.apache.hadoop.hbase.KeyValue; 25 import org.apache.hadoop.hbase.util.Bytes; 26 import org.apache.hadoop.io.Writable; 27 28 import java.io.DataInput; 29 import java.io.DataOutput; 30 import java.io.IOException; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Map; 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 public class Delete extends Mutation 68 implements Writable, Comparable<Row> { 69 private static final byte DELETE_VERSION = (byte)3; 70 71 /** Constructor for Writable. DO NOT USE */ 72 public Delete() { 73 this((byte [])null); 74 } 75 76 /** 77 * Create a Delete operation for the specified row. 78 * <p> 79 * If no further operations are done, this will delete everything 80 * associated with the specified row (all versions of all columns in all 81 * families). 82 * @param row row key 83 */ 84 public Delete(byte [] row) { 85 this(row, HConstants.LATEST_TIMESTAMP); 86 } 87 88 /** 89 * Create a Delete operation for the specified row and timestamp.<p> 90 * 91 * If no further operations are done, this will delete all columns in all 92 * families of the specified row with a timestamp less than or equal to the 93 * specified timestamp.<p> 94 * 95 * This timestamp is ONLY used for a delete row operation. If specifying 96 * families or columns, you must specify each timestamp individually. 97 * @param row row key 98 * @param timestamp maximum version timestamp (only for delete row) 99 */ 100 public Delete(byte [] row, long timestamp) { 101 this.row = row; 102 this.ts = timestamp; 103 } 104 105 /** 106 * Create a Delete operation for the specified row and timestamp, using 107 * an optional row lock.<p> 108 * 109 * If no further operations are done, this will delete all columns in all 110 * families of the specified row with a timestamp less than or equal to the 111 * specified timestamp.<p> 112 * 113 * This timestamp is ONLY used for a delete row operation. If specifying 114 * families or columns, you must specify each timestamp individually. 115 * @param row row key 116 * @param timestamp maximum version timestamp (only for delete row) 117 * @param rowLock previously acquired row lock, or null 118 * @deprecated {@link RowLock} is deprecated, use {@link #Delete(byte[], long)}. 119 */ 120 public Delete(byte [] row, long timestamp, RowLock rowLock) { 121 this.row = row; 122 this.ts = timestamp; 123 if (rowLock != null) { 124 this.lockId = rowLock.getLockId(); 125 } 126 } 127 128 /** 129 * @param d Delete to clone. 130 */ 131 public Delete(final Delete d) { 132 this.row = d.getRow(); 133 this.ts = d.getTimeStamp(); 134 this.lockId = d.getLockId(); 135 this.familyMap.putAll(d.getFamilyMap()); 136 this.writeToWAL = d.writeToWAL; 137 } 138 139 /** 140 * Advanced use only. Add an existing delete marker to this Delete object. 141 * @param kv An existing 'delete' tpye KeyValue - can be family, column, or point delete 142 * @return this for invocation chaining 143 * @throws IOException 144 */ 145 public Delete addDeleteMarker(KeyValue kv) throws IOException { 146 if (!(kv.isDelete() || kv.isDeleteColumnOrFamily())) { 147 throw new IOException("The recently added KeyValue is not of type " 148 + "delete. Rowkey: " + Bytes.toStringBinary(this.row)); 149 } 150 if (!kv.matchingRow(row)) { 151 throw new IOException("The row in the recently added KeyValue " 152 + Bytes.toStringBinary(kv.getRow()) + " doesn't match the original one " 153 + Bytes.toStringBinary(this.row)); 154 } 155 byte [] family = kv.getFamily(); 156 List<KeyValue> list = familyMap.get(family); 157 if (list == null) { 158 list = new ArrayList<KeyValue>(); 159 } 160 list.add(kv); 161 familyMap.put(family, list); 162 return this; 163 } 164 165 /** 166 * Delete all versions of all columns of the specified family. 167 * <p> 168 * Overrides previous calls to deleteColumn and deleteColumns for the 169 * specified family. 170 * @param family family name 171 * @return this for invocation chaining 172 */ 173 public Delete deleteFamily(byte [] family) { 174 this.deleteFamily(family, HConstants.LATEST_TIMESTAMP); 175 return this; 176 } 177 178 /** 179 * Delete all columns of the specified family with a timestamp less than 180 * or equal to the specified timestamp. 181 * <p> 182 * Overrides previous calls to deleteColumn and deleteColumns for the 183 * specified family. 184 * @param family family name 185 * @param timestamp maximum version timestamp 186 * @return this for invocation chaining 187 */ 188 public Delete deleteFamily(byte [] family, long timestamp) { 189 List<KeyValue> list = familyMap.get(family); 190 if(list == null) { 191 list = new ArrayList<KeyValue>(); 192 } else if(!list.isEmpty()) { 193 list.clear(); 194 } 195 list.add(new KeyValue(row, family, null, timestamp, KeyValue.Type.DeleteFamily)); 196 familyMap.put(family, list); 197 return this; 198 } 199 200 /** 201 * Delete all versions of the specified column. 202 * @param family family name 203 * @param qualifier column qualifier 204 * @return this for invocation chaining 205 */ 206 public Delete deleteColumns(byte [] family, byte [] qualifier) { 207 this.deleteColumns(family, qualifier, HConstants.LATEST_TIMESTAMP); 208 return this; 209 } 210 211 /** 212 * Delete all versions of the specified column with a timestamp less than 213 * or equal to the specified timestamp. 214 * @param family family name 215 * @param qualifier column qualifier 216 * @param timestamp maximum version timestamp 217 * @return this for invocation chaining 218 */ 219 public Delete deleteColumns(byte [] family, byte [] qualifier, long timestamp) { 220 List<KeyValue> list = familyMap.get(family); 221 if (list == null) { 222 list = new ArrayList<KeyValue>(); 223 } 224 list.add(new KeyValue(this.row, family, qualifier, timestamp, 225 KeyValue.Type.DeleteColumn)); 226 familyMap.put(family, list); 227 return this; 228 } 229 230 /** 231 * Delete the latest version of the specified column. 232 * This is an expensive call in that on the server-side, it first does a 233 * get to find the latest versions timestamp. Then it adds a delete using 234 * the fetched cells timestamp. 235 * @param family family name 236 * @param qualifier column qualifier 237 * @return this for invocation chaining 238 */ 239 public Delete deleteColumn(byte [] family, byte [] qualifier) { 240 this.deleteColumn(family, qualifier, HConstants.LATEST_TIMESTAMP); 241 return this; 242 } 243 244 /** 245 * Delete the specified version of the specified column. 246 * @param family family name 247 * @param qualifier column qualifier 248 * @param timestamp version timestamp 249 * @return this for invocation chaining 250 */ 251 public Delete deleteColumn(byte [] family, byte [] qualifier, long timestamp) { 252 List<KeyValue> list = familyMap.get(family); 253 if(list == null) { 254 list = new ArrayList<KeyValue>(); 255 } 256 list.add(new KeyValue( 257 this.row, family, qualifier, timestamp, KeyValue.Type.Delete)); 258 familyMap.put(family, list); 259 return this; 260 } 261 262 /** 263 * Set the timestamp of the delete. 264 * 265 * @param timestamp 266 */ 267 public void setTimestamp(long timestamp) { 268 this.ts = timestamp; 269 } 270 271 @Override 272 public Map<String, Object> toMap(int maxCols) { 273 // we start with the fingerprint map and build on top of it. 274 Map<String, Object> map = super.toMap(maxCols); 275 // why is put not doing this? 276 map.put("ts", this.ts); 277 return map; 278 } 279 280 //Writable 281 public void readFields(final DataInput in) throws IOException { 282 int version = in.readByte(); 283 if (version > DELETE_VERSION) { 284 throw new IOException("version not supported"); 285 } 286 this.row = Bytes.readByteArray(in); 287 this.ts = in.readLong(); 288 this.lockId = in.readLong(); 289 if (version > 2) { 290 this.writeToWAL = in.readBoolean(); 291 } 292 this.familyMap.clear(); 293 int numFamilies = in.readInt(); 294 for(int i=0;i<numFamilies;i++) { 295 byte [] family = Bytes.readByteArray(in); 296 int numColumns = in.readInt(); 297 List<KeyValue> list = new ArrayList<KeyValue>(numColumns); 298 for(int j=0;j<numColumns;j++) { 299 KeyValue kv = new KeyValue(); 300 kv.readFields(in); 301 list.add(kv); 302 } 303 this.familyMap.put(family, list); 304 } 305 if (version > 1) { 306 readAttributes(in); 307 } 308 } 309 310 public void write(final DataOutput out) throws IOException { 311 out.writeByte(DELETE_VERSION); 312 Bytes.writeByteArray(out, this.row); 313 out.writeLong(this.ts); 314 out.writeLong(this.lockId); 315 out.writeBoolean(this.writeToWAL); 316 out.writeInt(familyMap.size()); 317 for(Map.Entry<byte [], List<KeyValue>> entry : familyMap.entrySet()) { 318 Bytes.writeByteArray(out, entry.getKey()); 319 List<KeyValue> list = entry.getValue(); 320 out.writeInt(list.size()); 321 for(KeyValue kv : list) { 322 kv.write(out); 323 } 324 } 325 writeAttributes(out); 326 } 327 }