HBASE-3921 Allow adding arbitrary blobs to Put
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1127782 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1a628ccd42
commit
03f55a3e89
|
@ -232,6 +232,7 @@ Release 0.91.0 - Unreleased
|
|||
HBASE-3841 HTable and HTableInterface docs are inconsistent with
|
||||
one another (Harsh J Chouraria)
|
||||
HBASE-2937 Facilitate Timeouts In HBase Client (Karthick Sankarachary)
|
||||
HBASE-3921 Allow adding arbitrary blobs to Put (dhruba borthakur)
|
||||
|
||||
TASKS
|
||||
HBASE-3559 Move report of split to master OFF the heartbeat channel
|
||||
|
|
|
@ -24,11 +24,14 @@ import org.apache.hadoop.hbase.HConstants;
|
|||
import org.apache.hadoop.hbase.KeyValue;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.io.Writable;
|
||||
import org.apache.hadoop.io.WritableUtils;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
@ -66,7 +69,7 @@ import java.util.TreeMap;
|
|||
* timestamp. The constructor timestamp is not referenced.
|
||||
*/
|
||||
public class Delete implements Writable, Row, Comparable<Row> {
|
||||
private static final byte DELETE_VERSION = (byte)1;
|
||||
private static final byte DELETE_VERSION = (byte)2;
|
||||
|
||||
private byte [] row = null;
|
||||
// This ts is only used when doing a deleteRow. Anything less,
|
||||
|
@ -75,6 +78,9 @@ public class Delete implements Writable, Row, Comparable<Row> {
|
|||
private final Map<byte [], List<KeyValue>> familyMap =
|
||||
new TreeMap<byte [], List<KeyValue>>(Bytes.BYTES_COMPARATOR);
|
||||
|
||||
// a opaque blob that can be passed into a Delete.
|
||||
private Map<String, byte[]> attributes;
|
||||
|
||||
/** Constructor for Writable. DO NOT USE */
|
||||
public Delete() {
|
||||
this((byte [])null);
|
||||
|
@ -283,6 +289,54 @@ public class Delete implements Writable, Row, Comparable<Row> {
|
|||
this.ts = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets arbitrary delete's attribute.
|
||||
* In case value = null attribute is removed from the attributes map.
|
||||
* @param name attribute name
|
||||
* @param value attribute value
|
||||
*/
|
||||
public void setAttribute(String name, byte[] value) {
|
||||
if (attributes == null && value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attributes == null) {
|
||||
attributes = new HashMap<String, byte[]>();
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
attributes.remove(name);
|
||||
if (attributes.isEmpty()) {
|
||||
this.attributes = null;
|
||||
}
|
||||
} else {
|
||||
attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets put's attribute
|
||||
* @param name attribute name
|
||||
* @return attribute value if attribute is set, <tt>null</tt> otherwise
|
||||
*/
|
||||
public byte[] getAttribute(String name) {
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all scan's attributes
|
||||
* @return unmodifiable map of all attributes
|
||||
*/
|
||||
public Map<String, byte[]> getAttributesMap() {
|
||||
if (attributes == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return Collections.unmodifiableMap(attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
@ -341,6 +395,17 @@ public class Delete implements Writable, Row, Comparable<Row> {
|
|||
}
|
||||
this.familyMap.put(family, list);
|
||||
}
|
||||
if (version > 1) {
|
||||
int numAttributes = in.readInt();
|
||||
if (numAttributes > 0) {
|
||||
this.attributes = new HashMap<String, byte[]>();
|
||||
for(int i=0; i<numAttributes; i++) {
|
||||
String name = WritableUtils.readString(in);
|
||||
byte[] value = Bytes.readByteArray(in);
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(final DataOutput out) throws IOException {
|
||||
|
@ -357,6 +422,15 @@ public class Delete implements Writable, Row, Comparable<Row> {
|
|||
kv.write(out);
|
||||
}
|
||||
}
|
||||
if (this.attributes == null) {
|
||||
out.writeInt(0);
|
||||
} else {
|
||||
out.writeInt(this.attributes.size());
|
||||
for (Map.Entry<String, byte[]> attr : this.attributes.entrySet()) {
|
||||
WritableUtils.writeString(out, attr.getKey());
|
||||
Bytes.writeByteArray(out, attr.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -386,6 +460,4 @@ public class Delete implements Writable, Row, Comparable<Row> {
|
|||
this.deleteColumn(parts[0], parts[1], HConstants.LATEST_TIMESTAMP);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -26,12 +26,15 @@ import org.apache.hadoop.hbase.io.HeapSize;
|
|||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.ClassSize;
|
||||
import org.apache.hadoop.io.Writable;
|
||||
import org.apache.hadoop.io.WritableUtils;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
@ -45,7 +48,7 @@ import java.util.TreeMap;
|
|||
* {@link #add(byte[], byte[], long, byte[]) add} if setting the timestamp.
|
||||
*/
|
||||
public class Put implements HeapSize, Writable, Row, Comparable<Row> {
|
||||
private static final byte PUT_VERSION = (byte)1;
|
||||
private static final byte PUT_VERSION = (byte)2;
|
||||
|
||||
private byte [] row = null;
|
||||
private long timestamp = HConstants.LATEST_TIMESTAMP;
|
||||
|
@ -55,6 +58,9 @@ public class Put implements HeapSize, Writable, Row, Comparable<Row> {
|
|||
private Map<byte [], List<KeyValue>> familyMap =
|
||||
new TreeMap<byte [], List<KeyValue>>(Bytes.BYTES_COMPARATOR);
|
||||
|
||||
// a opaque blob that can be passed into a Put.
|
||||
private Map<String, byte[]> attributes;
|
||||
|
||||
private static final long OVERHEAD = ClassSize.align(
|
||||
ClassSize.OBJECT + ClassSize.REFERENCE +
|
||||
2 * Bytes.SIZEOF_LONG + Bytes.SIZEOF_BOOLEAN +
|
||||
|
@ -407,6 +413,56 @@ public class Put implements HeapSize, Writable, Row, Comparable<Row> {
|
|||
this.writeToWAL = write;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets arbitrary put's attribute.
|
||||
* In case value = null attribute is removed from the attributes map.
|
||||
* @param name attribute name
|
||||
* @param value attribute value
|
||||
*/
|
||||
public void setAttribute(String name, byte[] value) {
|
||||
if (attributes == null && value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attributes == null) {
|
||||
attributes = new HashMap<String, byte[]>();
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
attributes.remove(name);
|
||||
if (attributes.isEmpty()) {
|
||||
this.attributes = null;
|
||||
}
|
||||
} else {
|
||||
attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets put's attribute
|
||||
* @param name attribute name
|
||||
* @return attribute value if attribute is set, <tt>null</tt> otherwise
|
||||
*/
|
||||
public byte[] getAttribute(String name) {
|
||||
if (attributes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all scan's attributes
|
||||
* @return unmodifiable map of all attributes
|
||||
*/
|
||||
public Map<String, byte[]> getAttributesMap() {
|
||||
if (attributes == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
return Collections.unmodifiableMap(attributes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
|
@ -502,6 +558,17 @@ public class Put implements HeapSize, Writable, Row, Comparable<Row> {
|
|||
}
|
||||
this.familyMap.put(family, keys);
|
||||
}
|
||||
if (version > 1) {
|
||||
int numAttributes = in.readInt();
|
||||
if (numAttributes > 0) {
|
||||
this.attributes = new HashMap<String, byte[]>();
|
||||
for(int i=0; i<numAttributes; i++) {
|
||||
String name = WritableUtils.readString(in);
|
||||
byte[] value = Bytes.readByteArray(in);
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void write(final DataOutput out)
|
||||
|
@ -526,6 +593,15 @@ public class Put implements HeapSize, Writable, Row, Comparable<Row> {
|
|||
out.write(kv.getBuffer(), kv.getOffset(), kv.getLength());
|
||||
}
|
||||
}
|
||||
if (this.attributes == null) {
|
||||
out.writeInt(0);
|
||||
} else {
|
||||
out.writeInt(this.attributes.size());
|
||||
for (Map.Entry<String, byte[]> attr : this.attributes.entrySet()) {
|
||||
WritableUtils.writeString(out, attr.getKey());
|
||||
Bytes.writeByteArray(out, attr.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* Copyright 2009 The Apache Software Foundation
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.hbase.client;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestAttributes {
|
||||
@Test
|
||||
public void testAttributesSerialization() throws IOException {
|
||||
Put put = new Put();
|
||||
put.setAttribute("attribute1", Bytes.toBytes("value1"));
|
||||
put.setAttribute("attribute2", Bytes.toBytes("value2"));
|
||||
put.setAttribute("attribute3", Bytes.toBytes("value3"));
|
||||
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
DataOutput out = new DataOutputStream(byteArrayOutputStream);
|
||||
put.write(out);
|
||||
|
||||
Put put2 = new Put();
|
||||
Assert.assertTrue(put2.getAttributesMap().isEmpty());
|
||||
|
||||
put2.readFields(new DataInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())));
|
||||
|
||||
Assert.assertNull(put2.getAttribute("absent"));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value1"), put2.getAttribute("attribute1")));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value2"), put2.getAttribute("attribute2")));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value3"), put2.getAttribute("attribute3")));
|
||||
Assert.assertEquals(3, put2.getAttributesMap().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutAttributes() {
|
||||
Put put = new Put();
|
||||
Assert.assertTrue(put.getAttributesMap().isEmpty());
|
||||
Assert.assertNull(put.getAttribute("absent"));
|
||||
|
||||
put.setAttribute("absent", null);
|
||||
Assert.assertTrue(put.getAttributesMap().isEmpty());
|
||||
Assert.assertNull(put.getAttribute("absent"));
|
||||
|
||||
// adding attribute
|
||||
put.setAttribute("attribute1", Bytes.toBytes("value1"));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value1"), put.getAttribute("attribute1")));
|
||||
Assert.assertEquals(1, put.getAttributesMap().size());
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value1"), put.getAttributesMap().get("attribute1")));
|
||||
|
||||
// overriding attribute value
|
||||
put.setAttribute("attribute1", Bytes.toBytes("value12"));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value12"), put.getAttribute("attribute1")));
|
||||
Assert.assertEquals(1, put.getAttributesMap().size());
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value12"), put.getAttributesMap().get("attribute1")));
|
||||
|
||||
// adding another attribute
|
||||
put.setAttribute("attribute2", Bytes.toBytes("value2"));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value2"), put.getAttribute("attribute2")));
|
||||
Assert.assertEquals(2, put.getAttributesMap().size());
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value2"), put.getAttributesMap().get("attribute2")));
|
||||
|
||||
// removing attribute
|
||||
put.setAttribute("attribute2", null);
|
||||
Assert.assertNull(put.getAttribute("attribute2"));
|
||||
Assert.assertEquals(1, put.getAttributesMap().size());
|
||||
Assert.assertNull(put.getAttributesMap().get("attribute2"));
|
||||
|
||||
// removing non-existed attribute
|
||||
put.setAttribute("attribute2", null);
|
||||
Assert.assertNull(put.getAttribute("attribute2"));
|
||||
Assert.assertEquals(1, put.getAttributesMap().size());
|
||||
Assert.assertNull(put.getAttributesMap().get("attribute2"));
|
||||
|
||||
// removing another attribute
|
||||
put.setAttribute("attribute1", null);
|
||||
Assert.assertNull(put.getAttribute("attribute1"));
|
||||
Assert.assertTrue(put.getAttributesMap().isEmpty());
|
||||
Assert.assertNull(put.getAttributesMap().get("attribute1"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteAttributes() {
|
||||
Delete del = new Delete();
|
||||
Assert.assertTrue(del.getAttributesMap().isEmpty());
|
||||
Assert.assertNull(del.getAttribute("absent"));
|
||||
|
||||
del.setAttribute("absent", null);
|
||||
Assert.assertTrue(del.getAttributesMap().isEmpty());
|
||||
Assert.assertNull(del.getAttribute("absent"));
|
||||
|
||||
// adding attribute
|
||||
del.setAttribute("attribute1", Bytes.toBytes("value1"));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value1"), del.getAttribute("attribute1")));
|
||||
Assert.assertEquals(1, del.getAttributesMap().size());
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value1"), del.getAttributesMap().get("attribute1")));
|
||||
|
||||
// overriding attribute value
|
||||
del.setAttribute("attribute1", Bytes.toBytes("value12"));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value12"), del.getAttribute("attribute1")));
|
||||
Assert.assertEquals(1, del.getAttributesMap().size());
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value12"), del.getAttributesMap().get("attribute1")));
|
||||
|
||||
// adding another attribute
|
||||
del.setAttribute("attribute2", Bytes.toBytes("value2"));
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value2"), del.getAttribute("attribute2")));
|
||||
Assert.assertEquals(2, del.getAttributesMap().size());
|
||||
Assert.assertTrue(Arrays.equals(Bytes.toBytes("value2"), del.getAttributesMap().get("attribute2")));
|
||||
|
||||
// removing attribute
|
||||
del.setAttribute("attribute2", null);
|
||||
Assert.assertNull(del.getAttribute("attribute2"));
|
||||
Assert.assertEquals(1, del.getAttributesMap().size());
|
||||
Assert.assertNull(del.getAttributesMap().get("attribute2"));
|
||||
|
||||
// removing non-existed attribute
|
||||
del.setAttribute("attribute2", null);
|
||||
Assert.assertNull(del.getAttribute("attribute2"));
|
||||
Assert.assertEquals(1, del.getAttributesMap().size());
|
||||
Assert.assertNull(del.getAttributesMap().get("attribute2"));
|
||||
|
||||
// removing another attribute
|
||||
del.setAttribute("attribute1", null);
|
||||
Assert.assertNull(del.getAttribute("attribute1"));
|
||||
Assert.assertTrue(del.getAttributesMap().isEmpty());
|
||||
Assert.assertNull(del.getAttributesMap().get("attribute1"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue