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 package org.apache.hadoop.hbase.client;
20 
21 import java.io.IOException;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.NavigableMap;
25 import java.util.TreeMap;
26 import java.util.UUID;
27 
28 import org.apache.hadoop.hbase.classification.InterfaceAudience;
29 import org.apache.hadoop.hbase.classification.InterfaceStability;
30 import org.apache.hadoop.hbase.Cell;
31 import org.apache.hadoop.hbase.CellUtil;
32 import org.apache.hadoop.hbase.KeyValue;
33 import org.apache.hadoop.hbase.io.TimeRange;
34 import org.apache.hadoop.hbase.security.access.Permission;
35 import org.apache.hadoop.hbase.security.visibility.CellVisibility;
36 import org.apache.hadoop.hbase.util.Bytes;
37 import org.apache.hadoop.hbase.util.ClassSize;
38 
39 /**
40  * Used to perform Increment operations on a single row.
41  * <p>
42  * This operation does not appear atomic to readers.  Increments are done
43  * under a single row lock, so write operations to a row are synchronized, but
44  * readers do not take row locks so get and scan operations can see this
45  * operation partially completed.
46  * <p>
47  * To increment columns of a row, instantiate an Increment object with the row
48  * to increment.  At least one column to increment must be specified using the
49  * {@link #addColumn(byte[], byte[], long)} method.
50  */
51 @InterfaceAudience.Public
52 @InterfaceStability.Stable
53 public class Increment extends Mutation implements Comparable<Row> {
54   private static final long HEAP_OVERHEAD =  ClassSize.REFERENCE + ClassSize.TIMERANGE;
55   private TimeRange tr = new TimeRange();
56 
57   /**
58    * Create a Increment operation for the specified row.
59    * <p>
60    * At least one column must be incremented.
61    * @param row row key (we will make a copy of this).
62    */
Increment(byte [] row)63   public Increment(byte [] row) {
64     this(row, 0, row.length);
65   }
66 
67   /**
68    * Create a Increment operation for the specified row.
69    * <p>
70    * At least one column must be incremented.
71    * @param row row key (we will make a copy of this).
72    */
Increment(final byte [] row, final int offset, final int length)73   public Increment(final byte [] row, final int offset, final int length) {
74     checkRow(row, offset, length);
75     this.row = Bytes.copy(row, offset, length);
76   }
77   /**
78    * Copy constructor
79    * @param i
80    */
Increment(Increment i)81   public Increment(Increment i) {
82     this.row = i.getRow();
83     this.ts = i.getTimeStamp();
84     this.tr = i.getTimeRange();
85     this.familyMap.putAll(i.getFamilyCellMap());
86     for (Map.Entry<String, byte[]> entry : i.getAttributesMap().entrySet()) {
87       this.setAttribute(entry.getKey(), entry.getValue());
88     }
89   }
90 
91   /**
92    * Add the specified KeyValue to this operation.
93    * @param cell individual Cell
94    * @return this
95    * @throws java.io.IOException e
96    */
add(Cell cell)97   public Increment add(Cell cell) throws IOException{
98     byte [] family = CellUtil.cloneFamily(cell);
99     List<Cell> list = getCellList(family);
100     //Checking that the row of the kv is the same as the put
101     int res = Bytes.compareTo(this.row, 0, row.length,
102         cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
103     if (res != 0) {
104       throw new WrongRowIOException("The row in " + cell +
105         " doesn't match the original one " +  Bytes.toStringBinary(this.row));
106     }
107     list.add(cell);
108     familyMap.put(family, list);
109     return this;
110   }
111 
112   /**
113    * Increment the column from the specific family with the specified qualifier
114    * by the specified amount.
115    * <p>
116    * Overrides previous calls to addColumn for this family and qualifier.
117    * @param family family name
118    * @param qualifier column qualifier
119    * @param amount amount to increment by
120    * @return the Increment object
121    */
addColumn(byte [] family, byte [] qualifier, long amount)122   public Increment addColumn(byte [] family, byte [] qualifier, long amount) {
123     if (family == null) {
124       throw new IllegalArgumentException("family cannot be null");
125     }
126     if (qualifier == null) {
127       throw new IllegalArgumentException("qualifier cannot be null");
128     }
129     List<Cell> list = getCellList(family);
130     KeyValue kv = createPutKeyValue(family, qualifier, ts, Bytes.toBytes(amount));
131     list.add(kv);
132     familyMap.put(CellUtil.cloneFamily(kv), list);
133     return this;
134   }
135 
136   /**
137    * Gets the TimeRange used for this increment.
138    * @return TimeRange
139    */
getTimeRange()140   public TimeRange getTimeRange() {
141     return this.tr;
142   }
143 
144   /**
145    * Sets the TimeRange to be used on the Get for this increment.
146    * <p>
147    * This is useful for when you have counters that only last for specific
148    * periods of time (ie. counters that are partitioned by time).  By setting
149    * the range of valid times for this increment, you can potentially gain
150    * some performance with a more optimal Get operation.
151    * <p>
152    * This range is used as [minStamp, maxStamp).
153    * @param minStamp minimum timestamp value, inclusive
154    * @param maxStamp maximum timestamp value, exclusive
155    * @throws IOException if invalid time range
156    * @return this
157    */
setTimeRange(long minStamp, long maxStamp)158   public Increment setTimeRange(long minStamp, long maxStamp)
159   throws IOException {
160     tr = new TimeRange(minStamp, maxStamp);
161     return this;
162   }
163 
164   /**
165    * @param returnResults True (default) if the increment operation should return the results. A
166    *          client that is not interested in the result can save network bandwidth setting this
167    *          to false.
168    */
setReturnResults(boolean returnResults)169   public Increment setReturnResults(boolean returnResults) {
170     super.setReturnResults(returnResults);
171     return this;
172   }
173 
174   /**
175    * @return current setting for returnResults
176    */
177   // This method makes public the superclasses's protected method.
isReturnResults()178   public boolean isReturnResults() {
179     return super.isReturnResults();
180   }
181 
182   /**
183    * Method for retrieving the number of families to increment from
184    * @return number of families
185    */
186   @Override
numFamilies()187   public int numFamilies() {
188     return this.familyMap.size();
189   }
190 
191   /**
192    * Method for checking if any families have been inserted into this Increment
193    * @return true if familyMap is non empty false otherwise
194    */
hasFamilies()195   public boolean hasFamilies() {
196     return !this.familyMap.isEmpty();
197   }
198 
199   /**
200    * Before 0.95, when you called Increment#getFamilyMap(), you got back
201    * a map of families to a list of Longs.  Now, {@link #getFamilyCellMap()} returns
202    * families by list of Cells.  This method has been added so you can have the
203    * old behavior.
204    * @return Map of families to a Map of qualifiers and their Long increments.
205    * @since 0.95.0
206    */
getFamilyMapOfLongs()207   public Map<byte[], NavigableMap<byte [], Long>> getFamilyMapOfLongs() {
208     NavigableMap<byte[], List<Cell>> map = super.getFamilyCellMap();
209     Map<byte [], NavigableMap<byte[], Long>> results =
210       new TreeMap<byte[], NavigableMap<byte [], Long>>(Bytes.BYTES_COMPARATOR);
211     for (Map.Entry<byte [], List<Cell>> entry: map.entrySet()) {
212       NavigableMap<byte [], Long> longs = new TreeMap<byte [], Long>(Bytes.BYTES_COMPARATOR);
213       for (Cell cell: entry.getValue()) {
214         longs.put(CellUtil.cloneQualifier(cell),
215             Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
216       }
217       results.put(entry.getKey(), longs);
218     }
219     return results;
220   }
221 
222   /**
223    * @return String
224    */
225   @Override
toString()226   public String toString() {
227     StringBuilder sb = new StringBuilder();
228     sb.append("row=");
229     sb.append(Bytes.toStringBinary(this.row));
230     if(this.familyMap.size() == 0) {
231       sb.append(", no columns set to be incremented");
232       return sb.toString();
233     }
234     sb.append(", families=");
235     boolean moreThanOne = false;
236     for(Map.Entry<byte [], List<Cell>> entry: this.familyMap.entrySet()) {
237       if(moreThanOne) {
238         sb.append("), ");
239       } else {
240         moreThanOne = true;
241         sb.append("{");
242       }
243       sb.append("(family=");
244       sb.append(Bytes.toString(entry.getKey()));
245       sb.append(", columns=");
246       if(entry.getValue() == null) {
247         sb.append("NONE");
248       } else {
249         sb.append("{");
250         boolean moreThanOneB = false;
251         for(Cell cell : entry.getValue()) {
252           if(moreThanOneB) {
253             sb.append(", ");
254           } else {
255             moreThanOneB = true;
256           }
257           sb.append(CellUtil.getCellKeyAsString(cell) + "+=" +
258               Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
259         }
260         sb.append("}");
261       }
262     }
263     sb.append("}");
264     return sb.toString();
265   }
266 
267   @Override
compareTo(Row i)268   public int compareTo(Row i) {
269     // TODO: This is wrong.  Can't have two the same just because on same row.
270     return Bytes.compareTo(this.getRow(), i.getRow());
271   }
272 
273   @Override
hashCode()274   public int hashCode() {
275     // TODO: This is wrong.  Can't have two gets the same just because on same row.  But it
276     // matches how equals works currently and gets rid of the findbugs warning.
277     return Bytes.hashCode(this.getRow());
278   }
279 
280   @Override
equals(Object obj)281   public boolean equals(Object obj) {
282     // TODO: This is wrong.  Can't have two the same just because on same row.
283     if (this == obj) {
284       return true;
285     }
286     if (obj == null || getClass() != obj.getClass()) {
287       return false;
288     }
289     Row other = (Row) obj;
290     return compareTo(other) == 0;
291   }
292 
293   @Override
extraHeapSize()294   protected long extraHeapSize(){
295     return HEAP_OVERHEAD;
296   }
297 
298   @Override
setAttribute(String name, byte[] value)299   public Increment setAttribute(String name, byte[] value) {
300     return (Increment) super.setAttribute(name, value);
301   }
302 
303   @Override
setId(String id)304   public Increment setId(String id) {
305     return (Increment) super.setId(id);
306   }
307 
308   @Override
309   @Deprecated
setWriteToWAL(boolean write)310   public Increment setWriteToWAL(boolean write) {
311     return (Increment) super.setWriteToWAL(write);
312   }
313 
314   @Override
setDurability(Durability d)315   public Increment setDurability(Durability d) {
316     return (Increment) super.setDurability(d);
317   }
318 
319   @Override
setFamilyCellMap(NavigableMap<byte[], List<Cell>> map)320   public Increment setFamilyCellMap(NavigableMap<byte[], List<Cell>> map) {
321     return (Increment) super.setFamilyCellMap(map);
322   }
323 
324   @Override
325   @Deprecated
setFamilyMap(NavigableMap<byte[], List<KeyValue>> map)326   public Increment setFamilyMap(NavigableMap<byte[], List<KeyValue>> map) {
327     return (Increment) super.setFamilyMap(map);
328   }
329 
330   @Override
setClusterIds(List<UUID> clusterIds)331   public Increment setClusterIds(List<UUID> clusterIds) {
332     return (Increment) super.setClusterIds(clusterIds);
333   }
334 
335   @Override
setCellVisibility(CellVisibility expression)336   public Increment setCellVisibility(CellVisibility expression) {
337     return (Increment) super.setCellVisibility(expression);
338   }
339 
340   @Override
setACL(String user, Permission perms)341   public Increment setACL(String user, Permission perms) {
342     return (Increment) super.setACL(user, perms);
343   }
344 
345   @Override
setACL(Map<String, Permission> perms)346   public Increment setACL(Map<String, Permission> perms) {
347     return (Increment) super.setACL(perms);
348   }
349 
350   @Override
setTTL(long ttl)351   public Increment setTTL(long ttl) {
352     return (Increment) super.setTTL(ttl);
353   }
354 }
355