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.hbase.classification.InterfaceAudience;
28 import org.apache.hadoop.hbase.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 for (Map.Entry<String, byte[]> entry : d.getAttributesMap().entrySet()) {
144 this.setAttribute(entry.getKey(), entry.getValue());
145 }
146 }
147
148 /**
149 * Advanced use only.
150 * Add an existing delete marker to this Delete object.
151 * @param kv An existing KeyValue of type "delete".
152 * @return this for invocation chaining
153 * @throws IOException
154 */
155 @SuppressWarnings("unchecked")
156 public Delete addDeleteMarker(Cell kv) throws IOException {
157 // TODO: Deprecate and rename 'add' so it matches how we add KVs to Puts.
158 if (!CellUtil.isDelete(kv)) {
159 throw new IOException("The recently added KeyValue is not of type "
160 + "delete. Rowkey: " + Bytes.toStringBinary(this.row));
161 }
162 if (Bytes.compareTo(this.row, 0, row.length, kv.getRowArray(),
163 kv.getRowOffset(), kv.getRowLength()) != 0) {
164 throw new WrongRowIOException("The row in " + kv.toString() +
165 " doesn't match the original one " + Bytes.toStringBinary(this.row));
166 }
167 byte [] family = CellUtil.cloneFamily(kv);
168 List<Cell> list = familyMap.get(family);
169 if (list == null) {
170 list = new ArrayList<Cell>();
171 }
172 list.add(kv);
173 familyMap.put(family, list);
174 return this;
175 }
176
177 /**
178 * Delete all versions of all columns of the specified family.
179 * <p>
180 * Overrides previous calls to deleteColumn and deleteColumns for the
181 * specified family.
182 * @param family family name
183 * @return this for invocation chaining
184 */
185 public Delete deleteFamily(byte [] family) {
186 this.deleteFamily(family, this.ts);
187 return this;
188 }
189
190 /**
191 * Delete all columns of the specified family with a timestamp less than
192 * or equal to the specified timestamp.
193 * <p>
194 * Overrides previous calls to deleteColumn and deleteColumns for the
195 * specified family.
196 * @param family family name
197 * @param timestamp maximum version timestamp
198 * @return this for invocation chaining
199 */
200 @SuppressWarnings("unchecked")
201 public Delete deleteFamily(byte [] family, long timestamp) {
202 if (timestamp < 0) {
203 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
204 }
205 List<Cell> list = familyMap.get(family);
206 if(list == null) {
207 list = new ArrayList<Cell>();
208 } else if(!list.isEmpty()) {
209 list.clear();
210 }
211 KeyValue kv = new KeyValue(row, family, null, timestamp, KeyValue.Type.DeleteFamily);
212 list.add(kv);
213 familyMap.put(family, list);
214 return this;
215 }
216
217 /**
218 * Delete all columns of the specified family with a timestamp equal to
219 * the specified timestamp.
220 * @param family family name
221 * @param timestamp version timestamp
222 * @return this for invocation chaining
223 */
224 public Delete deleteFamilyVersion(byte [] family, long timestamp) {
225 List<Cell> list = familyMap.get(family);
226 if(list == null) {
227 list = new ArrayList<Cell>();
228 }
229 list.add(new KeyValue(row, family, null, timestamp,
230 KeyValue.Type.DeleteFamilyVersion));
231 familyMap.put(family, list);
232 return this;
233 }
234
235
236 /**
237 * Delete all versions of the specified column.
238 * @param family family name
239 * @param qualifier column qualifier
240 * @return this for invocation chaining
241 */
242 public Delete deleteColumns(byte [] family, byte [] qualifier) {
243 this.deleteColumns(family, qualifier, this.ts);
244 return this;
245 }
246
247 /**
248 * Delete all versions of the specified column with a timestamp less than
249 * or equal to the specified timestamp.
250 * @param family family name
251 * @param qualifier column qualifier
252 * @param timestamp maximum version timestamp
253 * @return this for invocation chaining
254 */
255 @SuppressWarnings("unchecked")
256 public Delete deleteColumns(byte [] family, byte [] qualifier, long timestamp) {
257 if (timestamp < 0) {
258 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
259 }
260 List<Cell> list = familyMap.get(family);
261 if (list == null) {
262 list = new ArrayList<Cell>();
263 }
264 list.add(new KeyValue(this.row, family, qualifier, timestamp,
265 KeyValue.Type.DeleteColumn));
266 familyMap.put(family, list);
267 return this;
268 }
269
270 /**
271 * Delete the latest version of the specified column.
272 * This is an expensive call in that on the server-side, it first does a
273 * get to find the latest versions timestamp. Then it adds a delete using
274 * the fetched cells timestamp.
275 * @param family family name
276 * @param qualifier column qualifier
277 * @return this for invocation chaining
278 */
279 public Delete deleteColumn(byte [] family, byte [] qualifier) {
280 this.deleteColumn(family, qualifier, this.ts);
281 return this;
282 }
283
284 /**
285 * Delete the specified version of the specified column.
286 * @param family family name
287 * @param qualifier column qualifier
288 * @param timestamp version timestamp
289 * @return this for invocation chaining
290 */
291 @SuppressWarnings("unchecked")
292 public Delete deleteColumn(byte [] family, byte [] qualifier, long timestamp) {
293 if (timestamp < 0) {
294 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
295 }
296 List<Cell> list = familyMap.get(family);
297 if(list == null) {
298 list = new ArrayList<Cell>();
299 }
300 KeyValue kv = new KeyValue(this.row, family, qualifier, timestamp, KeyValue.Type.Delete);
301 list.add(kv);
302 familyMap.put(family, list);
303 return this;
304 }
305
306 /**
307 * Set the timestamp of the delete.
308 *
309 * @param timestamp
310 */
311 public void setTimestamp(long timestamp) {
312 if (timestamp < 0) {
313 throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + timestamp);
314 }
315 this.ts = timestamp;
316 }
317
318 @Override
319 public Map<String, Object> toMap(int maxCols) {
320 // we start with the fingerprint map and build on top of it.
321 Map<String, Object> map = super.toMap(maxCols);
322 // why is put not doing this?
323 map.put("ts", this.ts);
324 return map;
325 }
326 }