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.HConstants;
31 import org.apache.hadoop.hbase.KeyValue;
32 import org.apache.hadoop.hbase.util.Bytes;
33
34 /**
35 * Used to perform Delete operations on a single row.
36 * <p>
37 * To delete an entire row, instantiate a Delete object with the row
38 * to delete. To further define the scope of what to delete, perform
39 * additional methods as outlined below.
40 * <p>
41 * To delete specific families, execute {@link #deleteFamily(byte[]) deleteFamily}
42 * for each family to delete.
43 * <p>
44 * To delete multiple versions of specific columns, execute
45 * {@link #deleteColumns(byte[], byte[]) deleteColumns}
46 * for each column to delete.
47 * <p>
48 * To delete specific versions of specific columns, execute
49 * {@link #deleteColumn(byte[], byte[], long) deleteColumn}
50 * for each column version to delete.
51 * <p>
52 * Specifying timestamps, deleteFamily and deleteColumns will delete all
53 * versions with a timestamp less than or equal to that passed. If no
54 * timestamp is specified, an entry is added with a timestamp of 'now'
55 * where 'now' is the servers's System.currentTimeMillis().
56 * Specifying a timestamp to the deleteColumn method will
57 * delete versions only with a timestamp equal to that specified.
58 * If no timestamp is passed to deleteColumn, internally, it figures the
59 * most recent cell's timestamp and adds a delete at that timestamp; i.e.
60 * it deletes the most recently added cell.
61 * <p>The timestamp passed to the constructor is used ONLY for delete of
62 * rows. For anything less -- a deleteColumn, deleteColumns or
63 * deleteFamily -- then you need to use the method overrides that take a
64 * timestamp. The constructor timestamp is not referenced.
65 */
66 @InterfaceAudience.Public
67 @InterfaceStability.Stable
68 public class Delete extends Mutation implements Comparable<Row> {
69 /**
70 * Create a Delete operation for the specified row.
71 * <p>
72 * If no further operations are done, this will delete everything
73 * associated with the specified row (all versions of all columns in all
74 * families).
75 * @param row row key
76 */
77 public Delete(byte [] row) {
78 this(row, HConstants.LATEST_TIMESTAMP);
79 }
80
81 /**
82 * Create a Delete operation for the specified row and timestamp.<p>
83 *
84 * If no further operations are done, this will delete all columns in all
85 * families of the specified row with a timestamp less than or equal to the
86 * specified timestamp.<p>
87 *
88 * This timestamp is ONLY used for a delete row operation. If specifying
89 * families or columns, you must specify each timestamp individually.
90 * @param row row key
91 * @param timestamp maximum version timestamp (only for delete row)
92 */
93 public Delete(byte [] row, long timestamp) {
94 this(row, 0, row.length, timestamp);
95 }
96
97 /**
98 * Create a Delete operation for the specified row and timestamp.<p>
99 *
100 * If no further operations are done, this will delete all columns in all
101 * families of the specified row with a timestamp less than or equal to the
102 * specified timestamp.<p>
103 *
104 * This timestamp is ONLY used for a delete row operation. If specifying
105 * families or columns, you must specify each timestamp individually.
106 * @param rowArray We make a local copy of this passed in row.
107 * @param rowOffset
108 * @param rowLength
109 * @param ts maximum version timestamp (only for delete row)
110 */
111 public Delete(final byte [] rowArray, final int rowOffset, final int rowLength, long ts) {
112 checkRow(rowArray, rowOffset, rowLength);
113 this.row = Bytes.copy(rowArray, rowOffset, rowLength);
114 this.ts = ts;
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.familyMap.putAll(d.getFamilyMap());
124 this.durability = d.durability;
125 }
126
127 /**
128 * Advanced use only.
129 * Add an existing delete marker to this Delete object.
130 * @param kv An existing KeyValue of type "delete".
131 * @return this for invocation chaining
132 * @throws IOException
133 */
134 @SuppressWarnings("unchecked")
135 public Delete addDeleteMarker(KeyValue kv) throws IOException {
136 // TODO: Deprecate and rename 'add' so it matches how we add KVs to Puts.
137 if (!kv.isDelete()) {
138 throw new IOException("The recently added KeyValue is not of type "
139 + "delete. Rowkey: " + Bytes.toStringBinary(this.row));
140 }
141 if (Bytes.compareTo(this.row, 0, row.length, kv.getBuffer(),
142 kv.getRowOffset(), kv.getRowLength()) != 0) {
143 throw new WrongRowIOException("The row in " + kv.toString() +
144 " doesn't match the original one " + Bytes.toStringBinary(this.row));
145 }
146 byte [] family = kv.getFamily();
147 List<? extends Cell> list = familyMap.get(family);
148 if (list == null) {
149 list = new ArrayList<Cell>();
150 }
151 // Cast so explicit list type rather than ? extends Cell. Help the compiler out. See
152 // http://stackoverflow.com/questions/6474784/java-using-generics-with-lists-and-interfaces
153 ((List<KeyValue>)list).add(kv);
154 familyMap.put(family, list);
155 return this;
156 }
157
158 /**
159 * Delete all versions of all columns of the specified family.
160 * <p>
161 * Overrides previous calls to deleteColumn and deleteColumns for the
162 * specified family.
163 * @param family family name
164 * @return this for invocation chaining
165 */
166 public Delete deleteFamily(byte [] family) {
167 this.deleteFamily(family, HConstants.LATEST_TIMESTAMP);
168 return this;
169 }
170
171 /**
172 * Delete all columns of the specified family with a timestamp less than
173 * or equal to the specified timestamp.
174 * <p>
175 * Overrides previous calls to deleteColumn and deleteColumns for the
176 * specified family.
177 * @param family family name
178 * @param timestamp maximum version timestamp
179 * @return this for invocation chaining
180 */
181 @SuppressWarnings("unchecked")
182 public Delete deleteFamily(byte [] family, long timestamp) {
183 List<? extends Cell> list = familyMap.get(family);
184 if(list == null) {
185 list = new ArrayList<Cell>();
186 } else if(!list.isEmpty()) {
187 list.clear();
188 }
189 KeyValue kv = new KeyValue(row, family, null, timestamp, KeyValue.Type.DeleteFamily);
190 // Cast so explicit list type rather than ? extends Cell. Help the compiler out. See
191 // http://stackoverflow.com/questions/6474784/java-using-generics-with-lists-and-interfaces
192 ((List<KeyValue>)list).add(kv);
193 familyMap.put(family, list);
194 return this;
195 }
196
197 /**
198 * Delete all versions of the specified column.
199 * @param family family name
200 * @param qualifier column qualifier
201 * @return this for invocation chaining
202 */
203 public Delete deleteColumns(byte [] family, byte [] qualifier) {
204 this.deleteColumns(family, qualifier, HConstants.LATEST_TIMESTAMP);
205 return this;
206 }
207
208 /**
209 * Delete all versions of the specified column with a timestamp less than
210 * or equal to the specified timestamp.
211 * @param family family name
212 * @param qualifier column qualifier
213 * @param timestamp maximum version timestamp
214 * @return this for invocation chaining
215 */
216 @SuppressWarnings("unchecked")
217 public Delete deleteColumns(byte [] family, byte [] qualifier, long timestamp) {
218 List<? extends Cell> list = familyMap.get(family);
219 if (list == null) {
220 list = new ArrayList<Cell>();
221 }
222 // Cast so explicit list type rather than ? extends Cell. Help the compiler out. See
223 // http://stackoverflow.com/questions/6474784/java-using-generics-with-lists-and-interfaces
224 ((List<KeyValue>)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 @SuppressWarnings("unchecked")
252 public Delete deleteColumn(byte [] family, byte [] qualifier, long timestamp) {
253 List<? extends Cell> list = familyMap.get(family);
254 if(list == null) {
255 list = new ArrayList<Cell>();
256 }
257 // Cast so explicit list type rather than ? extends Cell. Help the compiler out. See
258 // http://stackoverflow.com/questions/6474784/java-using-generics-with-lists-and-interfaces
259 KeyValue kv = new KeyValue(this.row, family, qualifier, timestamp, KeyValue.Type.Delete);
260 ((List<KeyValue>)list).add(kv);
261 familyMap.put(family, list);
262 return this;
263 }
264
265 /**
266 * Set the timestamp of the delete.
267 *
268 * @param timestamp
269 */
270 public void setTimestamp(long timestamp) {
271 this.ts = timestamp;
272 }
273
274 @Override
275 public Map<String, Object> toMap(int maxCols) {
276 // we start with the fingerprint map and build on top of it.
277 Map<String, Object> map = super.toMap(maxCols);
278 // why is put not doing this?
279 map.put("ts", this.ts);
280 return map;
281 }
282 }