View Javadoc

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.nio.ByteBuffer;
24  import java.util.ArrayList;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.TreeMap;
28  
29  import org.apache.hadoop.classification.InterfaceAudience;
30  import org.apache.hadoop.classification.InterfaceStability;
31  import org.apache.hadoop.hbase.Cell;
32  import org.apache.hadoop.hbase.CellUtil;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.KeyValue;
35  import org.apache.hadoop.hbase.Tag;
36  import org.apache.hadoop.hbase.io.HeapSize;
37  import org.apache.hadoop.hbase.util.Bytes;
38  
39  /**
40   * Used to perform Put operations for a single row.
41   * <p>
42   * To perform a Put, instantiate a Put object with the row to insert to and
43   * for eachumn to be inserted, execute {@link #add(byte[], byte[], byte[]) add} or
44   * {@link #add(byte[], byte[], long, byte[]) add} if setting the timestamp.
45   */
46  @InterfaceAudience.Public
47  @InterfaceStability.Stable
48  public class Put extends Mutation implements HeapSize, Comparable<Row> {
49    /**
50     * Create a Put operation for the specified row.
51     * @param row row key
52     */
53    public Put(byte [] row) {
54      this(row, HConstants.LATEST_TIMESTAMP);
55    }
56  
57    /**
58     * Create a Put operation for the specified row, using a given timestamp.
59     *
60     * @param row row key; we make a copy of what we are passed to keep local.
61     * @param ts timestamp
62     */
63    public Put(byte[] row, long ts) {
64      this(row, 0, row.length, ts);
65    }
66  
67    /**
68     * We make a copy of the passed in row key to keep local.
69     * @param rowArray
70     * @param rowOffset
71     * @param rowLength
72     */
73    public Put(byte [] rowArray, int rowOffset, int rowLength) {
74      this(rowArray, rowOffset, rowLength, HConstants.LATEST_TIMESTAMP);
75    }
76  
77    /**
78     * @param row row key; we make a copy of what we are passed to keep local.
79     * @param ts  timestamp
80     */
81    public Put(ByteBuffer row, long ts) {
82      if (ts < 0) {
83        throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
84      }
85      checkRow(row);
86      this.row = new byte[row.remaining()];
87      row.get(this.row);
88      this.ts = ts;
89    }
90  
91    /**
92     * @param row row key; we make a copy of what we are passed to keep local.
93     */
94    public Put(ByteBuffer row) {
95      this(row, HConstants.LATEST_TIMESTAMP);
96    }
97  
98    /**
99     * We make a copy of the passed in row key to keep local.
100    * @param rowArray
101    * @param rowOffset
102    * @param rowLength
103    * @param ts
104    */
105   public Put(byte [] rowArray, int rowOffset, int rowLength, long ts) {
106     checkRow(rowArray, rowOffset, rowLength);
107     this.row = Bytes.copy(rowArray, rowOffset, rowLength);
108     this.ts = ts;
109     if (ts < 0) {
110       throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
111     }
112   }
113 
114   /**
115    * Copy constructor.  Creates a Put operation cloned from the specified Put.
116    * @param putToCopy put to copy
117    */
118   public Put(Put putToCopy) {
119     this(putToCopy.getRow(), putToCopy.ts);
120     this.familyMap = new TreeMap<byte [], List<Cell>>(Bytes.BYTES_COMPARATOR);
121     for(Map.Entry<byte [], List<Cell>> entry: putToCopy.getFamilyCellMap().entrySet()) {
122       this.familyMap.put(entry.getKey(), entry.getValue());
123     }
124     this.durability = putToCopy.durability;
125     for (Map.Entry<String, byte[]> entry : putToCopy.getAttributesMap().entrySet()) {
126       this.setAttribute(entry.getKey(), entry.getValue());
127     }
128   }
129 
130   /**
131    * Add the specified column and value to this Put operation.
132    * @param family family name
133    * @param qualifier column qualifier
134    * @param value column value
135    * @return this
136    */
137   public Put add(byte [] family, byte [] qualifier, byte [] value) {
138     return add(family, qualifier, this.ts, value);
139   }
140 
141   /**
142    * See {@link #add(byte[], byte[], byte[])}. This version expects
143    * that the underlying arrays won't change. It's intended
144    * for usage internal HBase to and for advanced client applications.
145    */
146   public Put addImmutable(byte [] family, byte [] qualifier, byte [] value) {
147     return addImmutable(family, qualifier, this.ts, value);
148   }
149 
150   /**
151    * This expects that the underlying arrays won't change. It's intended
152    * for usage internal HBase to and for advanced client applications.
153    */
154   public Put addImmutable(byte[] family, byte [] qualifier, byte [] value, Tag[] tag) {
155     return addImmutable(family, qualifier, this.ts, value, tag);
156   }
157 
158   /**
159    * Add the specified column and value, with the specified timestamp as
160    * its version to this Put operation.
161    * @param family family name
162    * @param qualifier column qualifier
163    * @param ts version timestamp
164    * @param value column value
165    * @return this
166    */
167   public Put add(byte [] family, byte [] qualifier, long ts, byte [] value) {
168     if (ts < 0) {
169       throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
170     }
171     List<Cell> list = getCellList(family);
172     KeyValue kv = createPutKeyValue(family, qualifier, ts, value);
173     list.add(kv);
174     familyMap.put(CellUtil.cloneFamily(kv), list);
175     return this;
176   }
177 
178   /**
179    * See {@link #add(byte[], byte[], long, byte[])}. This version expects
180    * that the underlying arrays won't change. It's intended
181    * for usage internal HBase to and for advanced client applications.
182    */
183   public Put addImmutable(byte [] family, byte [] qualifier, long ts, byte [] value) {
184     if (ts < 0) {
185       throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
186     }
187     List<Cell> list = getCellList(family);
188     KeyValue kv = createPutKeyValue(family, qualifier, ts, value);
189     list.add(kv);
190     familyMap.put(family, list);
191     return this;
192   }
193 
194   /**
195    * This expects that the underlying arrays won't change. It's intended
196    * for usage internal HBase to and for advanced client applications.
197    */
198   @SuppressWarnings("unchecked")
199   public Put addImmutable(byte[] family, byte[] qualifier, long ts, byte[] value, Tag[] tag) {
200     List<Cell> list = getCellList(family);
201     KeyValue kv = createPutKeyValue(family, qualifier, ts, value, tag);
202     list.add(kv);
203     familyMap.put(family, list);
204     return this;
205   }
206 
207   /**
208    * This expects that the underlying arrays won't change. It's intended
209    * for usage internal HBase to and for advanced client applications.
210    */
211   public Put addImmutable(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value,
212                           Tag[] tag) {
213     if (ts < 0) {
214       throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
215     }
216     List<Cell> list = getCellList(family);
217     KeyValue kv = createPutKeyValue(family, qualifier, ts, value, tag);
218     list.add(kv);
219     familyMap.put(family, list);
220     return this;
221   }
222 
223 
224   /**
225    * Add the specified column and value, with the specified timestamp as
226    * its version to this Put operation.
227    * @param family family name
228    * @param qualifier column qualifier
229    * @param ts version timestamp
230    * @param value column value
231    * @return this
232    */
233   public Put add(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value) {
234     if (ts < 0) {
235       throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
236     }
237     List<Cell> list = getCellList(family);
238     KeyValue kv = createPutKeyValue(family, qualifier, ts, value, null);
239     list.add(kv);
240     familyMap.put(CellUtil.cloneFamily(kv), list);
241     return this;
242   }
243 
244   /**
245    * See {@link #add(byte[], ByteBuffer, long, ByteBuffer)}. This version expects
246    * that the underlying arrays won't change. It's intended
247    * for usage internal HBase to and for advanced client applications.
248    */
249   public Put addImmutable(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value) {
250     if (ts < 0) {
251       throw new IllegalArgumentException("Timestamp cannot be negative. ts=" + ts);
252     }
253     List<Cell> list = getCellList(family);
254     KeyValue kv = createPutKeyValue(family, qualifier, ts, value, null);
255     list.add(kv);
256     familyMap.put(family, list);
257     return this;
258   }
259 
260   /**
261    * Add the specified KeyValue to this Put operation.  Operation assumes that
262    * the passed KeyValue is immutable and its backing array will not be modified
263    * for the duration of this Put.
264    * @param kv individual KeyValue
265    * @return this
266    * @throws java.io.IOException e
267    */
268   public Put add(Cell kv) throws IOException{
269     byte [] family = CellUtil.cloneFamily(kv);
270     List<Cell> list = getCellList(family);
271     //Checking that the row of the kv is the same as the put
272     int res = Bytes.compareTo(this.row, 0, row.length,
273         kv.getRowArray(), kv.getRowOffset(), kv.getRowLength());
274     if (res != 0) {
275       throw new WrongRowIOException("The row in " + kv.toString() +
276         " doesn't match the original one " +  Bytes.toStringBinary(this.row));
277     }
278     list.add(kv);
279     familyMap.put(family, list);
280     return this;
281   }
282 
283   /**
284    * A convenience method to determine if this object's familyMap contains
285    * a value assigned to the given family & qualifier.
286    * Both given arguments must match the KeyValue object to return true.
287    *
288    * @param family column family
289    * @param qualifier column qualifier
290    * @return returns true if the given family and qualifier already has an
291    * existing KeyValue object in the family map.
292    */
293   public boolean has(byte [] family, byte [] qualifier) {
294   return has(family, qualifier, this.ts, new byte[0], true, true);
295   }
296 
297   /**
298    * A convenience method to determine if this object's familyMap contains
299    * a value assigned to the given family, qualifier and timestamp.
300    * All 3 given arguments must match the KeyValue object to return true.
301    *
302    * @param family column family
303    * @param qualifier column qualifier
304    * @param ts timestamp
305    * @return returns true if the given family, qualifier and timestamp already has an
306    * existing KeyValue object in the family map.
307    */
308   public boolean has(byte [] family, byte [] qualifier, long ts) {
309   return has(family, qualifier, ts, new byte[0], false, true);
310   }
311 
312   /**
313    * A convenience method to determine if this object's familyMap contains
314    * a value assigned to the given family, qualifier and timestamp.
315    * All 3 given arguments must match the KeyValue object to return true.
316    *
317    * @param family column family
318    * @param qualifier column qualifier
319    * @param value value to check
320    * @return returns true if the given family, qualifier and value already has an
321    * existing KeyValue object in the family map.
322    */
323   public boolean has(byte [] family, byte [] qualifier, byte [] value) {
324     return has(family, qualifier, this.ts, value, true, false);
325   }
326 
327   /**
328    * A convenience method to determine if this object's familyMap contains
329    * the given value assigned to the given family, qualifier and timestamp.
330    * All 4 given arguments must match the KeyValue object to return true.
331    *
332    * @param family column family
333    * @param qualifier column qualifier
334    * @param ts timestamp
335    * @param value value to check
336    * @return returns true if the given family, qualifier timestamp and value
337    * already has an existing KeyValue object in the family map.
338    */
339   public boolean has(byte [] family, byte [] qualifier, long ts, byte [] value) {
340       return has(family, qualifier, ts, value, false, false);
341   }
342 
343   /*
344    * Private method to determine if this object's familyMap contains
345    * the given value assigned to the given family, qualifier and timestamp
346    * respecting the 2 boolean arguments
347    *
348    * @param family
349    * @param qualifier
350    * @param ts
351    * @param value
352    * @param ignoreTS
353    * @param ignoreValue
354    * @return returns true if the given family, qualifier timestamp and value
355    * already has an existing KeyValue object in the family map.
356    */
357   private boolean has(byte[] family, byte[] qualifier, long ts, byte[] value,
358                       boolean ignoreTS, boolean ignoreValue) {
359     List<Cell> list = getCellList(family);
360     if (list.size() == 0) {
361       return false;
362     }
363     // Boolean analysis of ignoreTS/ignoreValue.
364     // T T => 2
365     // T F => 3 (first is always true)
366     // F T => 2
367     // F F => 1
368     if (!ignoreTS && !ignoreValue) {
369       for (Cell cell : list) {
370         if (CellUtil.matchingFamily(cell, family) &&
371             CellUtil.matchingQualifier(cell, qualifier)  &&
372             CellUtil.matchingValue(cell, value) &&
373             cell.getTimestamp() == ts) {
374           return true;
375         }
376       }
377     } else if (ignoreValue && !ignoreTS) {
378       for (Cell cell : list) {
379         if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier)
380             && cell.getTimestamp() == ts) {
381           return true;
382         }
383       }
384     } else if (!ignoreValue && ignoreTS) {
385       for (Cell cell : list) {
386         if (CellUtil.matchingFamily(cell, family) && CellUtil.matchingQualifier(cell, qualifier)
387             && CellUtil.matchingValue(cell, value)) {
388           return true;
389         }
390       }
391     } else {
392       for (Cell cell : list) {
393         if (CellUtil.matchingFamily(cell, family) &&
394             CellUtil.matchingQualifier(cell, qualifier)) {
395           return true;
396         }
397       }
398     }
399     return false;
400   }
401 
402   /**
403    * Returns a list of all KeyValue objects with matching column family and qualifier.
404    *
405    * @param family column family
406    * @param qualifier column qualifier
407    * @return a list of KeyValue objects with the matching family and qualifier,
408    * returns an empty list if one doesn't exist for the given family.
409    */
410   public List<Cell> get(byte[] family, byte[] qualifier) {
411     List<Cell> filteredList = new ArrayList<Cell>();
412     for (Cell cell: getCellList(family)) {
413       if (CellUtil.matchingQualifier(cell, qualifier)) {
414         filteredList.add(cell);
415       }
416     }
417     return filteredList;
418   }
419 }