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