View Javadoc

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.
141    * Add an existing delete marker to this Delete object.
142    * @param kv An existing KeyValue of type "delete".
143    * @return this for invocation chaining
144    * @throws IOException
145    */
146   public Delete addDeleteMarker(KeyValue kv) throws IOException {
147     if (!kv.isDelete()) {
148       throw new IOException("The recently added KeyValue is not of type "
149           + "delete. Rowkey: " + Bytes.toStringBinary(this.row));
150     }
151     if (Bytes.compareTo(this.row, 0, row.length, kv.getBuffer(),
152         kv.getRowOffset(), kv.getRowLength()) != 0) {
153       throw new IOException("The row in the recently added KeyValue "
154           + Bytes.toStringBinary(kv.getBuffer(), kv.getRowOffset(),
155               kv.getRowLength()) + " doesn't match the original one "
156           + Bytes.toStringBinary(this.row));
157     }
158     byte [] family = kv.getFamily();
159     List<KeyValue> list = familyMap.get(family);
160     if (list == null) {
161       list = new ArrayList<KeyValue>();
162     }
163     list.add(kv);
164     familyMap.put(family, list);
165     return this;
166   }
167 
168   /**
169    * Delete all versions of all columns of the specified family.
170    * <p>
171    * Overrides previous calls to deleteColumn and deleteColumns for the
172    * specified family.
173    * @param family family name
174    * @return this for invocation chaining
175    */
176   public Delete deleteFamily(byte [] family) {
177     this.deleteFamily(family, HConstants.LATEST_TIMESTAMP);
178     return this;
179   }
180 
181   /**
182    * Delete all columns of the specified family with a timestamp less than
183    * or equal to the specified timestamp.
184    * <p>
185    * Overrides previous calls to deleteColumn and deleteColumns for the
186    * specified family.
187    * @param family family name
188    * @param timestamp maximum version timestamp
189    * @return this for invocation chaining
190    */
191   public Delete deleteFamily(byte [] family, long timestamp) {
192     List<KeyValue> list = familyMap.get(family);
193     if(list == null) {
194       list = new ArrayList<KeyValue>();
195     } else if(!list.isEmpty()) {
196       list.clear();
197     }
198     list.add(new KeyValue(row, family, null, timestamp, KeyValue.Type.DeleteFamily));
199     familyMap.put(family, list);
200     return this;
201   }
202 
203   /**
204    * Delete all versions of the specified column.
205    * @param family family name
206    * @param qualifier column qualifier
207    * @return this for invocation chaining
208    */
209   public Delete deleteColumns(byte [] family, byte [] qualifier) {
210     this.deleteColumns(family, qualifier, HConstants.LATEST_TIMESTAMP);
211     return this;
212   }
213 
214   /**
215    * Delete all versions of the specified column with a timestamp less than
216    * or equal to the specified timestamp.
217    * @param family family name
218    * @param qualifier column qualifier
219    * @param timestamp maximum version timestamp
220    * @return this for invocation chaining
221    */
222   public Delete deleteColumns(byte [] family, byte [] qualifier, long timestamp) {
223     List<KeyValue> list = familyMap.get(family);
224     if (list == null) {
225       list = new ArrayList<KeyValue>();
226     }
227     list.add(new KeyValue(this.row, family, qualifier, timestamp,
228       KeyValue.Type.DeleteColumn));
229     familyMap.put(family, list);
230     return this;
231   }
232 
233   /**
234    * Delete the latest version of the specified column.
235    * This is an expensive call in that on the server-side, it first does a
236    * get to find the latest versions timestamp.  Then it adds a delete using
237    * the fetched cells timestamp.
238    * @param family family name
239    * @param qualifier column qualifier
240    * @return this for invocation chaining
241    */
242   public Delete deleteColumn(byte [] family, byte [] qualifier) {
243     this.deleteColumn(family, qualifier, HConstants.LATEST_TIMESTAMP);
244     return this;
245   }
246 
247   /**
248    * Delete the specified version of the specified column.
249    * @param family family name
250    * @param qualifier column qualifier
251    * @param timestamp version timestamp
252    * @return this for invocation chaining
253    */
254   public Delete deleteColumn(byte [] family, byte [] qualifier, long timestamp) {
255     List<KeyValue> list = familyMap.get(family);
256     if(list == null) {
257       list = new ArrayList<KeyValue>();
258     }
259     list.add(new KeyValue(
260         this.row, family, qualifier, timestamp, KeyValue.Type.Delete));
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 
283   //Writable
284   public void readFields(final DataInput in) throws IOException {
285     int version = in.readByte();
286     if (version > DELETE_VERSION) {
287       throw new IOException("version not supported");
288     }
289     this.row = Bytes.readByteArray(in);
290     this.ts = in.readLong();
291     this.lockId = in.readLong();
292     if (version > 2) {
293       this.writeToWAL = in.readBoolean();
294     }
295     this.familyMap.clear();
296     int numFamilies = in.readInt();
297     for(int i=0;i<numFamilies;i++) {
298       byte [] family = Bytes.readByteArray(in);
299       int numColumns = in.readInt();
300       List<KeyValue> list = new ArrayList<KeyValue>(numColumns);
301       for(int j=0;j<numColumns;j++) {
302     	KeyValue kv = new KeyValue();
303     	kv.readFields(in);
304     	list.add(kv);
305       }
306       this.familyMap.put(family, list);
307     }
308     if (version > 1) {
309       readAttributes(in);
310     }
311   }
312 
313   public void write(final DataOutput out) throws IOException {
314     out.writeByte(DELETE_VERSION);
315     Bytes.writeByteArray(out, this.row);
316     out.writeLong(this.ts);
317     out.writeLong(this.lockId);
318     out.writeBoolean(this.writeToWAL);
319     out.writeInt(familyMap.size());
320     for(Map.Entry<byte [], List<KeyValue>> entry : familyMap.entrySet()) {
321       Bytes.writeByteArray(out, entry.getKey());
322       List<KeyValue> list = entry.getValue();
323       out.writeInt(list.size());
324       for(KeyValue kv : list) {
325         kv.write(out);
326       }
327     }
328     writeAttributes(out);
329   }
330 }