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 import java.util.TreeMap;
35
36 /**
37 * Used to perform Delete operations on a single row.
38 * <p>
39 * To delete an entire row, instantiate a Delete object with the row
40 * to delete. To further define the scope of what to delete, perform
41 * additional methods as outlined below.
42 * <p>
43 * To delete specific families, execute {@link #deleteFamily(byte[]) deleteFamily}
44 * for each family to delete.
45 * <p>
46 * To delete multiple versions of specific columns, execute
47 * {@link #deleteColumns(byte[], byte[]) deleteColumns}
48 * for each column to delete.
49 * <p>
50 * To delete specific versions of specific columns, execute
51 * {@link #deleteColumn(byte[], byte[], long) deleteColumn}
52 * for each column version to delete.
53 * <p>
54 * Specifying timestamps, deleteFamily and deleteColumns will delete all
55 * versions with a timestamp less than or equal to that passed. If no
56 * timestamp is specified, an entry is added with a timestamp of 'now'
57 * where 'now' is the servers's System.currentTimeMillis().
58 * Specifying a timestamp to the deleteColumn method will
59 * delete versions only with a timestamp equal to that specified.
60 * If no timestamp is passed to deleteColumn, internally, it figures the
61 * most recent cell's timestamp and adds a delete at that timestamp; i.e.
62 * it deletes the most recently added cell.
63 * <p>The timestamp passed to the constructor is used ONLY for delete of
64 * rows. For anything less -- a deleteColumn, deleteColumns or
65 * deleteFamily -- then you need to use the method overrides that take a
66 * timestamp. The constructor timestamp is not referenced.
67 */
68 public class Delete implements Writable, Row, Comparable<Row> {
69 private static final byte DELETE_VERSION = (byte)1;
70
71 private byte [] row = null;
72 // This ts is only used when doing a deleteRow. Anything less,
73 private long ts;
74 private long lockId = -1L;
75 private final Map<byte [], List<KeyValue>> familyMap =
76 new TreeMap<byte [], List<KeyValue>>(Bytes.BYTES_COMPARATOR);
77
78 /** Constructor for Writable. DO NOT USE */
79 public Delete() {
80 this((byte [])null);
81 }
82
83 /**
84 * Create a Delete operation for the specified row.
85 * <p>
86 * If no further operations are done, this will delete everything
87 * associated with the specified row (all versions of all columns in all
88 * families).
89 * @param row row key
90 */
91 public Delete(byte [] row) {
92 this(row, HConstants.LATEST_TIMESTAMP, null);
93 }
94
95 /**
96 * Create a Delete operation for the specified row and timestamp, using
97 * an optional row lock.<p>
98 *
99 * If no further operations are done, this will delete all columns in all
100 * families of the specified row with a timestamp less than or equal to the
101 * specified timestamp.<p>
102 *
103 * This timestamp is ONLY used for a delete row operation. If specifying
104 * families or columns, you must specify each timestamp individually.
105 * @param row row key
106 * @param timestamp maximum version timestamp (only for delete row)
107 * @param rowLock previously acquired row lock, or null
108 */
109 public Delete(byte [] row, long timestamp, RowLock rowLock) {
110 this.row = row;
111 this.ts = timestamp;
112 if (rowLock != null) {
113 this.lockId = rowLock.getLockId();
114 }
115 }
116
117 /**
118 * @param d Delete to clone.
119 */
120 public Delete(final Delete d) {
121 this.row = d.getRow();
122 this.ts = d.getTimeStamp();
123 this.lockId = d.getLockId();
124 this.familyMap.putAll(d.getFamilyMap());
125 }
126
127 public int compareTo(final Row d) {
128 return Bytes.compareTo(this.getRow(), d.getRow());
129 }
130
131 /**
132 * Method to check if the familyMap is empty
133 * @return true if empty, false otherwise
134 */
135 public boolean isEmpty() {
136 return familyMap.isEmpty();
137 }
138
139 /**
140 * Delete all versions of all columns of the specified family.
141 * <p>
142 * Overrides previous calls to deleteColumn and deleteColumns for the
143 * specified family.
144 * @param family family name
145 * @return this for invocation chaining
146 */
147 public Delete deleteFamily(byte [] family) {
148 this.deleteFamily(family, HConstants.LATEST_TIMESTAMP);
149 return this;
150 }
151
152 /**
153 * Delete all columns of the specified family with a timestamp less than
154 * or equal to the specified timestamp.
155 * <p>
156 * Overrides previous calls to deleteColumn and deleteColumns for the
157 * specified family.
158 * @param family family name
159 * @param timestamp maximum version timestamp
160 * @return this for invocation chaining
161 */
162 public Delete deleteFamily(byte [] family, long timestamp) {
163 List<KeyValue> list = familyMap.get(family);
164 if(list == null) {
165 list = new ArrayList<KeyValue>();
166 } else if(!list.isEmpty()) {
167 list.clear();
168 }
169 list.add(new KeyValue(row, family, null, timestamp, KeyValue.Type.DeleteFamily));
170 familyMap.put(family, list);
171 return this;
172 }
173
174 /**
175 * Delete all versions of the specified column.
176 * @param family family name
177 * @param qualifier column qualifier
178 * @return this for invocation chaining
179 */
180 public Delete deleteColumns(byte [] family, byte [] qualifier) {
181 this.deleteColumns(family, qualifier, HConstants.LATEST_TIMESTAMP);
182 return this;
183 }
184
185 /**
186 * Delete all versions of the specified column with a timestamp less than
187 * or equal to the specified timestamp.
188 * @param family family name
189 * @param qualifier column qualifier
190 * @param timestamp maximum version timestamp
191 * @return this for invocation chaining
192 */
193 public Delete deleteColumns(byte [] family, byte [] qualifier, long timestamp) {
194 List<KeyValue> list = familyMap.get(family);
195 if (list == null) {
196 list = new ArrayList<KeyValue>();
197 }
198 list.add(new KeyValue(this.row, family, qualifier, timestamp,
199 KeyValue.Type.DeleteColumn));
200 familyMap.put(family, list);
201 return this;
202 }
203
204 /**
205 * Delete the latest version of the specified column.
206 * This is an expensive call in that on the server-side, it first does a
207 * get to find the latest versions timestamp. Then it adds a delete using
208 * the fetched cells timestamp.
209 * @param family family name
210 * @param qualifier column qualifier
211 * @return this for invocation chaining
212 */
213 public Delete deleteColumn(byte [] family, byte [] qualifier) {
214 this.deleteColumn(family, qualifier, HConstants.LATEST_TIMESTAMP);
215 return this;
216 }
217
218 /**
219 * Delete the specified version of the specified column.
220 * @param family family name
221 * @param qualifier column qualifier
222 * @param timestamp version timestamp
223 * @return this for invocation chaining
224 */
225 public Delete deleteColumn(byte [] family, byte [] qualifier, long timestamp) {
226 List<KeyValue> list = familyMap.get(family);
227 if(list == null) {
228 list = new ArrayList<KeyValue>();
229 }
230 list.add(new KeyValue(
231 this.row, family, qualifier, timestamp, KeyValue.Type.Delete));
232 familyMap.put(family, list);
233 return this;
234 }
235
236 /**
237 * Method for retrieving the delete's familyMap
238 * @return familyMap
239 */
240 public Map<byte [], List<KeyValue>> getFamilyMap() {
241 return this.familyMap;
242 }
243
244 /**
245 * Method for retrieving the delete's row
246 * @return row
247 */
248 public byte [] getRow() {
249 return this.row;
250 }
251
252 /**
253 * Method for retrieving the delete's RowLock
254 * @return RowLock
255 */
256 public RowLock getRowLock() {
257 return new RowLock(this.row, this.lockId);
258 }
259
260 /**
261 * Method for retrieving the delete's lock ID.
262 *
263 * @return The lock ID.
264 */
265 public long getLockId() {
266 return this.lockId;
267 }
268
269 /**
270 * Method for retrieving the delete's timestamp
271 * @return timestamp
272 */
273 public long getTimeStamp() {
274 return this.ts;
275 }
276
277 /**
278 * @return string
279 */
280 @Override
281 public String toString() {
282 StringBuilder sb = new StringBuilder();
283 sb.append("row=");
284 sb.append(Bytes.toString(this.row));
285 sb.append(", ts=");
286 sb.append(this.ts);
287 sb.append(", families={");
288 boolean moreThanOne = false;
289 for(Map.Entry<byte [], List<KeyValue>> entry : this.familyMap.entrySet()) {
290 if(moreThanOne) {
291 sb.append(", ");
292 } else {
293 moreThanOne = true;
294 }
295 sb.append("(family=");
296 sb.append(Bytes.toString(entry.getKey()));
297 sb.append(", keyvalues=(");
298 boolean moreThanOneB = false;
299 for(KeyValue kv : entry.getValue()) {
300 if(moreThanOneB) {
301 sb.append(", ");
302 } else {
303 moreThanOneB = true;
304 }
305 sb.append(kv.toString());
306 }
307 sb.append(")");
308 }
309 sb.append("}");
310 return sb.toString();
311 }
312
313 //Writable
314 public void readFields(final DataInput in) throws IOException {
315 int version = in.readByte();
316 if (version > DELETE_VERSION) {
317 throw new IOException("version not supported");
318 }
319 this.row = Bytes.readByteArray(in);
320 this.ts = in.readLong();
321 this.lockId = in.readLong();
322 this.familyMap.clear();
323 int numFamilies = in.readInt();
324 for(int i=0;i<numFamilies;i++) {
325 byte [] family = Bytes.readByteArray(in);
326 int numColumns = in.readInt();
327 List<KeyValue> list = new ArrayList<KeyValue>(numColumns);
328 for(int j=0;j<numColumns;j++) {
329 KeyValue kv = new KeyValue();
330 kv.readFields(in);
331 list.add(kv);
332 }
333 this.familyMap.put(family, list);
334 }
335 }
336
337 public void write(final DataOutput out) throws IOException {
338 out.writeByte(DELETE_VERSION);
339 Bytes.writeByteArray(out, this.row);
340 out.writeLong(this.ts);
341 out.writeLong(this.lockId);
342 out.writeInt(familyMap.size());
343 for(Map.Entry<byte [], List<KeyValue>> entry : familyMap.entrySet()) {
344 Bytes.writeByteArray(out, entry.getKey());
345 List<KeyValue> list = entry.getValue();
346 out.writeInt(list.size());
347 for(KeyValue kv : list) {
348 kv.write(out);
349 }
350 }
351 }
352
353 /**
354 * Delete all versions of the specified column, given in
355 * <code>family:qualifier</code> notation, and with a timestamp less than
356 * or equal to the specified timestamp.
357 * @param column colon-delimited family and qualifier
358 * @param timestamp maximum version timestamp
359 * @deprecated use {@link #deleteColumn(byte[], byte[], long)} instead
360 * @return this for invocation chaining
361 */
362 public Delete deleteColumns(byte [] column, long timestamp) {
363 byte [][] parts = KeyValue.parseColumn(column);
364 this.deleteColumns(parts[0], parts[1], timestamp);
365 return this;
366 }
367
368 /**
369 * Delete the latest version of the specified column, given in
370 * <code>family:qualifier</code> notation.
371 * @param column colon-delimited family and qualifier
372 * @deprecated use {@link #deleteColumn(byte[], byte[])} instead
373 * @return this for invocation chaining
374 */
375 public Delete deleteColumn(byte [] column) {
376 byte [][] parts = KeyValue.parseColumn(column);
377 this.deleteColumn(parts[0], parts[1], HConstants.LATEST_TIMESTAMP);
378 return this;
379 }
380
381
382 }