You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by kt...@apache.org on 2013/07/31 18:52:03 UTC
[1/3] git commit: ACCUMULO-1626 fixed equals method in mutation
Updated Branches:
refs/heads/master 0b85105a4 -> a3d2673b3
ACCUMULO-1626 fixed equals method in mutation
Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/acc4fd16
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/acc4fd16
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/acc4fd16
Branch: refs/heads/master
Commit: acc4fd16f6390579c82e9635a3fa614f3822bfba
Parents: 3e97167
Author: Keith Turner <ke...@deenlo.com>
Authored: Wed Jul 31 11:48:15 2013 -0400
Committer: Keith Turner <ke...@deenlo.com>
Committed: Wed Jul 31 11:48:15 2013 -0400
----------------------------------------------------------------------
.../org/apache/accumulo/core/data/Mutation.java | 1 +
.../apache/accumulo/core/data/MutationTest.java | 69 ++++++++++++++++++++
2 files changed, 70 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/accumulo/blob/acc4fd16/src/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
----------------------------------------------------------------------
diff --git a/src/core/src/main/java/org/apache/accumulo/core/data/Mutation.java b/src/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
index 4e21b6d..3979da9 100644
--- a/src/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
+++ b/src/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
@@ -495,6 +495,7 @@ public class Mutation implements Writable {
public boolean equals(Mutation m) {
serialize();
+ m.serialize();
if (Arrays.equals(row, m.row) && entries == m.entries && Arrays.equals(data, m.data)) {
if (values == null && m.values == null)
return true;
http://git-wip-us.apache.org/repos/asf/accumulo/blob/acc4fd16/src/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
----------------------------------------------------------------------
diff --git a/src/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java b/src/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
index d50c15e..38ddcad 100644
--- a/src/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
+++ b/src/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
@@ -347,4 +347,73 @@ public class MutationTest extends TestCase {
assertEquals(m2.getUpdates().get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
}
+
+ public void testEquals() {
+ Mutation m1 = new Mutation("r1");
+
+ m1.put("cf1", "cq1", "v1");
+ m1.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
+ m1.put("cf1", "cq1", 3, "v3");
+ m1.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
+ m1.putDelete("cf2", "cf3");
+ m1.putDelete("cf2", "cf4", 3);
+ m1.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
+
+ // m2 has same data as m1
+ Mutation m2 = new Mutation("r1");
+
+ m2.put("cf1", "cq1", "v1");
+ m2.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
+ m2.put("cf1", "cq1", 3, "v3");
+ m2.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
+ m2.putDelete("cf2", "cf3");
+ m2.putDelete("cf2", "cf4", 3);
+ m2.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
+
+ // m3 has differnt row than m1
+ Mutation m3 = new Mutation("r2");
+
+ m3.put("cf1", "cq1", "v1");
+ m3.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
+ m3.put("cf1", "cq1", 3, "v3");
+ m3.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
+ m3.putDelete("cf2", "cf3");
+ m3.putDelete("cf2", "cf4", 3);
+ m3.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
+
+ // m4 has a different column than m1
+ Mutation m4 = new Mutation("r1");
+
+ m4.put("cf2", "cq1", "v1");
+ m4.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
+ m4.put("cf1", "cq1", 3, "v3");
+ m4.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
+ m4.putDelete("cf2", "cf3");
+ m4.putDelete("cf2", "cf4", 3);
+ m4.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
+
+ // m5 has a different value than m1
+ Mutation m5 = new Mutation("r1");
+
+ m5.put("cf1", "cq1", "v1");
+ m5.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
+ m5.put("cf1", "cq1", 3, "v4");
+ m5.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
+ m5.putDelete("cf2", "cf3");
+ m5.putDelete("cf2", "cf4", 3);
+ m5.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
+
+ assertEquals(m1, m1);
+ assertEquals(m1, m2);
+ assertEquals(m2, m1);
+ assertFalse(m1.equals(m3));
+ assertFalse(m3.equals(m1));
+ assertFalse(m1.equals(m4));
+ assertFalse(m4.equals(m1));
+ assertFalse(m3.equals(m4));
+ assertFalse(m1.equals(m5));
+ assertFalse(m5.equals(m1));
+ assertFalse(m3.equals(m5));
+ assertFalse(m4.equals(m5));
+ }
}
[3/3] git commit: Merge remote-tracking branch 'origin/1.5.1-SNAPSHOT'
Posted by kt...@apache.org.
Merge remote-tracking branch 'origin/1.5.1-SNAPSHOT'
Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/a3d2673b
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/a3d2673b
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/a3d2673b
Branch: refs/heads/master
Commit: a3d2673b312e4eba7c9518e70f552cc2e4764f70
Parents: 0b85105 6eddb39
Author: Keith Turner <kt...@apache.org>
Authored: Wed Jul 31 12:22:55 2013 -0400
Committer: Keith Turner <kt...@apache.org>
Committed: Wed Jul 31 12:22:55 2013 -0400
----------------------------------------------------------------------
.../org/apache/accumulo/core/data/Mutation.java | 1 +
.../apache/accumulo/core/data/MutationTest.java | 68 ++++++++++++++++++++
2 files changed, 69 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/accumulo/blob/a3d2673b/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
----------------------------------------------------------------------
[2/3] git commit: Merge remote-tracking branch
'origin/1.4.4-SNAPSHOT' into 1.5.1-SNAPSHOT
Posted by kt...@apache.org.
Merge remote-tracking branch 'origin/1.4.4-SNAPSHOT' into 1.5.1-SNAPSHOT
Conflicts:
core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
core/src/test/java/org/apache/accumulo/core/data/OldMutation.java
Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/6eddb392
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/6eddb392
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/6eddb392
Branch: refs/heads/master
Commit: 6eddb392a715436be7ce71ef8c383d8133e394c8
Parents: 8622b4d acc4fd1
Author: Keith Turner <kt...@apache.org>
Authored: Wed Jul 31 12:15:57 2013 -0400
Committer: Keith Turner <kt...@apache.org>
Committed: Wed Jul 31 12:15:57 2013 -0400
----------------------------------------------------------------------
.../org/apache/accumulo/core/data/Mutation.java | 1 +
.../apache/accumulo/core/data/MutationTest.java | 68 ++++++++++++++++++++
2 files changed, 69 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/accumulo/blob/6eddb392/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
----------------------------------------------------------------------
diff --cc core/src/main/java/org/apache/accumulo/core/data/Mutation.java
index 00cefbf,0000000..407dbc0
mode 100644,000000..100644
--- a/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
+++ b/core/src/main/java/org/apache/accumulo/core/data/Mutation.java
@@@ -1,721 -1,0 +1,722 @@@
+/*
+ * 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.accumulo.core.data;
+
+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.List;
+
+import org.apache.accumulo.core.data.thrift.TMutation;
+import org.apache.accumulo.core.security.ColumnVisibility;
+import org.apache.accumulo.core.util.ByteBufferUtil;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableUtils;
+
+/**
+ * <p>
+ * Mutation represents an action that manipulates a row in a table. A mutation holds a list of column/value pairs that represent an atomic set of modifications
+ * to make to a row.
+ *
+ * <p>
+ * Convenience methods which takes columns and value as CharSequence (String implements CharSequence) are provided. CharSequence is converted to UTF-8 by
+ * constructing a new Text object.
+ *
+ * <p>
+ * When always passing in the same data as a CharSequence/String, its probably more efficient to call the Text put methods. This way the data is only encoded
+ * once and only one Text object is created.
+ *
+ * <p>
+ * All of the put methods append data to the mutation, they do not overwrite anything that was previously put. The mutation holds a list of all column/values
+ * that were put into it. The putDelete() methods do not remove something that was previously added to the mutation, rather they indicate that Accumulo should
+ * insert a delete marker for that row column.
+ *
+ */
+
+public class Mutation implements Writable {
+
+ static final int VALUE_SIZE_COPY_CUTOFF = 1 << 15;
+
+ public static enum SERIALIZED_FORMAT {
+ VERSION1,
+ VERSION2
+ };
+
+ private boolean useOldDeserialize = false;
+ private byte[] row;
+ private byte[] data;
+ private int entries;
+ private List<byte[]> values;
+
+ // created this little class instead of using ByteArrayOutput stream and DataOutputStream
+ // because both are synchronized... lots of small syncs slow things down
+ private static class ByteBuffer {
+
+ int offset;
+ byte data[] = new byte[64];
+
+ private void reserve(int l) {
+ if (offset + l > data.length) {
+ int newSize = data.length * 2;
+ while (newSize <= offset + l)
+ newSize = newSize * 2;
+
+ byte[] newData = new byte[newSize];
+ System.arraycopy(data, 0, newData, 0, offset);
+ data = newData;
+ }
+
+ }
+
+ public void add(byte[] bytes, int off, int length) {
+ reserve(length);
+ System.arraycopy(bytes, off, data, offset, length);
+ offset += length;
+ }
+
+ void add(boolean b) {
+ reserve(1);
+ if (b)
+ data[offset++] = 1;
+ else
+ data[offset++] = 0;
+ }
+
+ public byte[] toArray() {
+ byte ret[] = new byte[offset];
+ System.arraycopy(data, 0, ret, 0, offset);
+ return ret;
+ }
+
+ public void writeVLong(long i) {
+ reserve(9);
+ if (i >= -112 && i <= 127) {
+ data[offset++] = (byte)i;
+ return;
+ }
+
+ int len = -112;
+ if (i < 0) {
+ i ^= -1L; // take one's complement'
+ len = -120;
+ }
+
+ long tmp = i;
+ while (tmp != 0) {
+ tmp = tmp >> 8;
+ len--;
+ }
+
+ data[offset++] = (byte)len;
+
+ len = (len < -120) ? -(len + 120) : -(len + 112);
+
+ for (int idx = len; idx != 0; idx--) {
+ int shiftbits = (idx - 1) * 8;
+ long mask = 0xFFL << shiftbits;
+ data[offset++] = (byte)((i & mask) >> shiftbits);
+ }
+ }
+ }
+
+ private static class SimpleReader {
+ int offset;
+ byte data[];
+
+ SimpleReader(byte b[]) {
+ this.data = b;
+ }
+
+ int readInt() {
+ return (data[offset++] << 24) + ((data[offset++] & 255) << 16) + ((data[offset++] & 255) << 8) + ((data[offset++] & 255) << 0);
+ }
+
+ long readLong() {
+ return (((long) data[offset++] << 56) + ((long) (data[offset++] & 255) << 48) + ((long) (data[offset++] & 255) << 40)
+ + ((long) (data[offset++] & 255) << 32) + ((long) (data[offset++] & 255) << 24) + ((data[offset++] & 255) << 16) + ((data[offset++] & 255) << 8) + ((data[offset++] & 255) << 0));
+ }
+
+ void readBytes(byte b[]) {
+ System.arraycopy(data, offset, b, 0, b.length);
+ offset += b.length;
+ }
+
+ boolean readBoolean() {
+ return (data[offset++] == 1);
+ }
+
+ long readVLong() {
+ byte firstByte = data[offset++];
+ int len = WritableUtils.decodeVIntSize(firstByte);
+ if (len == 1) {
+ return firstByte;
+ }
+ long i = 0;
+ for (int idx = 0; idx < len-1; idx++) {
+ byte b = data[offset++];
+ i = i << 8;
+ i = i | (b & 0xFF);
+ }
+ return (WritableUtils.isNegativeVInt(firstByte) ? (i ^ -1L) : i);
+ }
+ }
+
+ private ByteBuffer buffer;
+
+ private List<ColumnUpdate> updates;
+
+ private static final byte[] EMPTY_BYTES = new byte[0];
+
+ private void serialize() {
+ if (buffer != null) {
+ data = buffer.toArray();
+ buffer = null;
+ }
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public Mutation(byte[] row) {
+ this(row, 0, row.length);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public Mutation(byte[] row, int start, int length) {
+ this.row = new byte[length];
+ System.arraycopy(row, start, this.row, 0, length);
+ buffer = new ByteBuffer();
+ }
+
+ public Mutation(Text row) {
+ this(row.getBytes(), 0, row.getLength());
+ }
+
+ public Mutation(CharSequence row) {
+ this(new Text(row.toString()));
+ }
+
+ public Mutation() {}
+
+ public Mutation(TMutation tmutation) {
+ this.row = ByteBufferUtil.toBytes(tmutation.row);
+ this.data = ByteBufferUtil.toBytes(tmutation.data);
+ this.entries = tmutation.entries;
+ this.values = ByteBufferUtil.toBytesList(tmutation.values);
+ }
+
+ public Mutation(Mutation m) {
+ m.serialize();
+ this.row = m.row;
+ this.data = m.data;
+ this.entries = m.entries;
+ this.values = m.values;
+ }
+
+ public byte[] getRow() {
+ return row;
+ }
+
+ private void put(byte b[]) {
+ put(b, b.length);
+ }
+
+ private void put(byte b[], int length) {
+ buffer.writeVLong(length);
+ buffer.add(b, 0, length);
+ }
+
+ private void put(boolean b) {
+ buffer.add(b);
+ }
+
+ private void put(int i) {
+ buffer.writeVLong(i);
+ }
+
+ private void put(long l) {
+ buffer.writeVLong(l);
+ }
+
+ private void put(byte[] cf, byte[] cq, byte[] cv, boolean hasts, long ts, boolean deleted, byte[] val) {
+ put(cf, cf.length, cq, cq.length, cv, hasts, ts, deleted, val, val.length);
+ }
+
+ /*
+ * When dealing with Text object the length must be gotten from the object, not from the byte array.
+ */
+ private void put(Text cf, Text cq, byte[] cv, boolean hasts, long ts, boolean deleted, byte[] val) {
+ put(cf.getBytes(), cf.getLength(), cq.getBytes(), cq.getLength(), cv, hasts, ts, deleted, val, val.length);
+ }
+
+ private void put(byte[] cf, int cfLength, byte[] cq, int cqLength, byte[] cv, boolean hasts, long ts, boolean deleted, byte[] val, int valLength) {
+ if (buffer == null) {
+ throw new IllegalStateException("Can not add to mutation after serializing it");
+ }
+ put(cf, cfLength);
+ put(cq, cqLength);
+ put(cv);
+ put(hasts);
+ if (hasts) {
+ put(ts);
+ }
+ put(deleted);
+
+ if (valLength < VALUE_SIZE_COPY_CUTOFF) {
+ put(val, valLength);
+ } else {
+ if (values == null) {
+ values = new ArrayList<byte[]>();
+ }
+ byte copy[] = new byte[valLength];
+ System.arraycopy(val, 0, copy, 0, valLength);
+ values.add(copy);
+ put(-1 * values.size());
+ }
+
+ entries++;
+ }
+
+ private void put(CharSequence cf, CharSequence cq, byte[] cv, boolean hasts, long ts, boolean deleted, byte[] val) {
+ put(new Text(cf.toString()), new Text(cq.toString()), cv, hasts, ts, deleted, val);
+ }
+
+ private void put(Text cf, Text cq, byte[] cv, boolean hasts, long ts, boolean deleted, Text val) {
+ put(cf.getBytes(), cf.getLength(), cq.getBytes(), cq.getLength(), cv, hasts, ts, deleted, val.getBytes(), val.getLength());
+ }
+
+ private void put(CharSequence cf, CharSequence cq, byte[] cv, boolean hasts, long ts, boolean deleted, CharSequence val) {
+ put(new Text(cf.toString()), new Text(cq.toString()), cv, hasts, ts, deleted, new Text(val.toString()));
+ }
+
+ public void put(Text columnFamily, Text columnQualifier, Value value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, false, value.get());
+ }
+
+ public void put(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility, Value value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, false, value.get());
+ }
+
+ public void put(Text columnFamily, Text columnQualifier, long timestamp, Value value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, false, value.get());
+ }
+
+ public void put(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility, long timestamp, Value value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, false, value.get());
+ }
+
+ public void putDelete(Text columnFamily, Text columnQualifier) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, true, EMPTY_BYTES);
+ }
+
+ public void putDelete(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, true, EMPTY_BYTES);
+ }
+
+ public void putDelete(Text columnFamily, Text columnQualifier, long timestamp) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, true, EMPTY_BYTES);
+ }
+
+ public void putDelete(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility, long timestamp) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, true, EMPTY_BYTES);
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, Value value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, false, value.get());
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, Value value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, false, value.get());
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, long timestamp, Value value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, false, value.get());
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, long timestamp, Value value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, false, value.get());
+ }
+
+ public void putDelete(CharSequence columnFamily, CharSequence columnQualifier) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, true, EMPTY_BYTES);
+ }
+
+ public void putDelete(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, true, EMPTY_BYTES);
+ }
+
+ public void putDelete(CharSequence columnFamily, CharSequence columnQualifier, long timestamp) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, true, EMPTY_BYTES);
+ }
+
+ public void putDelete(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, long timestamp) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, true, EMPTY_BYTES);
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, CharSequence value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, false, value);
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, CharSequence value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, false, value);
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, long timestamp, CharSequence value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, false, value);
+ }
+
+ public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, long timestamp, CharSequence value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, false, value);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void put(byte[] columnFamily, byte[] columnQualifier, byte[] value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, false, value);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void put(byte[] columnFamily, byte[] columnQualifier, ColumnVisibility columnVisibility, byte[] value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, false, value);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void put(byte[] columnFamily, byte[] columnQualifier, long timestamp, byte[] value) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, false, value);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void put(byte[] columnFamily, byte[] columnQualifier, ColumnVisibility columnVisibility, long timestamp, byte[] value) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, false, value);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void putDelete(byte[] columnFamily, byte[] columnQualifier) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, true, EMPTY_BYTES);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void putDelete(byte[] columnFamily, byte[] columnQualifier, ColumnVisibility columnVisibility) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, true, EMPTY_BYTES);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void putDelete(byte[] columnFamily, byte[] columnQualifier, long timestamp) {
+ put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, true, EMPTY_BYTES);
+ }
+
+ /**
+ * @since 1.5.0
+ */
+ public void putDelete(byte[] columnFamily, byte[] columnQualifier, ColumnVisibility columnVisibility, long timestamp) {
+ put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, true, EMPTY_BYTES);
+ }
+
+ private byte[] oldReadBytes(SimpleReader in) {
+ int len = in.readInt();
+ if (len == 0)
+ return EMPTY_BYTES;
+
+ byte bytes[] = new byte[len];
+ in.readBytes(bytes);
+ return bytes;
+ }
+
+ private byte[] readBytes(SimpleReader in) {
+ int len = (int)in.readVLong();
+ if (len == 0)
+ return EMPTY_BYTES;
+
+ byte bytes[] = new byte[len];
+ in.readBytes(bytes);
+ return bytes;
+ }
+
+ public List<ColumnUpdate> getUpdates() {
+ serialize();
+
+ SimpleReader in = new SimpleReader(data);
+
+ if (updates == null) {
+ if (entries == 1) {
+ updates = Collections.singletonList(deserializeColumnUpdate(in));
+ } else {
+ ColumnUpdate[] tmpUpdates = new ColumnUpdate[entries];
+
+ for (int i = 0; i < entries; i++)
+ tmpUpdates[i] = deserializeColumnUpdate(in);
+
+ updates = Arrays.asList(tmpUpdates);
+ }
+ }
+
+ return updates;
+ }
+
+ protected ColumnUpdate newColumnUpdate(byte[] cf, byte[] cq, byte[] cv, boolean hasts, long ts, boolean deleted, byte[] val) {
+ return new ColumnUpdate(cf, cq, cv, hasts, ts, deleted, val);
+ }
+
+ private ColumnUpdate deserializeColumnUpdate(SimpleReader in) {
+ byte[] cf = readBytes(in);
+ byte[] cq = readBytes(in);
+ byte[] cv = readBytes(in);
+ boolean hasts = in.readBoolean();
+ long ts = 0;
+ if (hasts)
+ ts = in.readVLong();
+ boolean deleted = in.readBoolean();
+
+ byte[] val;
+ int valLen = (int)in.readVLong();
+
+ if (valLen < 0) {
+ val = values.get((-1 * valLen) - 1);
+ } else if (valLen == 0) {
+ val = EMPTY_BYTES;
+ } else {
+ val = new byte[valLen];
+ in.readBytes(val);
+ }
+
+ return newColumnUpdate(cf, cq, cv, hasts, ts, deleted, val);
+ }
+
+ private int cachedValLens = -1;
+
+ long getValueLengths() {
+ if (values == null)
+ return 0;
+
+ if (cachedValLens == -1) {
+ int tmpCVL = 0;
+ for (byte[] val : values)
+ tmpCVL += val.length;
+
+ cachedValLens = tmpCVL;
+ }
+
+ return cachedValLens;
+
+ }
+
+ public long numBytes() {
+ serialize();
+ return row.length + data.length + getValueLengths();
+ }
+
+ public long estimatedMemoryUsed() {
+ return numBytes() + 238;
+ }
+
+ /**
+ * @return the number of column value pairs added to the mutation
+ */
+ public int size() {
+ return entries;
+ }
+
+ @Override
+ public void readFields(DataInput in) throws IOException {
+
+ // Clear out cached column updates and value lengths so
+ // that we recalculate them based on the (potentially) new
+ // data we are about to read in.
+ updates = null;
+ cachedValLens = -1;
+ buffer = null;
+ useOldDeserialize = false;
+
+ byte first = in.readByte();
+ if ((first & 0x80) != 0x80) {
+ oldReadFields(first, in);
+ useOldDeserialize = true;
+ return;
+ }
+
+ int len = WritableUtils.readVInt(in);
+ row = new byte[len];
+ in.readFully(row);
+ len = WritableUtils.readVInt(in);
+ data = new byte[len];
+ in.readFully(data);
+ entries = WritableUtils.readVInt(in);
+
+ boolean valuesPresent = (first & 0x01) == 0x01;
+ if (!valuesPresent) {
+ values = null;
+ } else {
+ values = new ArrayList<byte[]>();
+ int numValues = WritableUtils.readVInt(in);
+ for (int i = 0; i < numValues; i++) {
+ len = WritableUtils.readVInt(in);
+ byte val[] = new byte[len];
+ in.readFully(val);
+ values.add(val);
+ }
+ }
+ }
+
+ protected void droppingOldTimestamp(long ts) {}
+
+ private void oldReadFields(byte first, DataInput in) throws IOException {
+
+ byte b = (byte)in.readByte();
+ byte c = (byte)in.readByte();
+ byte d = (byte)in.readByte();
+
+ int len = (((first & 0xff) << 24) | ((b & 0xff) << 16) |
+ ((c & 0xff) << 8) | (d & 0xff));
+ row = new byte[len];
+ in.readFully(row);
+ len = in.readInt();
+ byte[] localData = new byte[len];
+ in.readFully(localData);
+ int localEntries = in.readInt();
+
+ List<byte[]> localValues;
+ boolean valuesPresent = in.readBoolean();
+ if (!valuesPresent) {
+ localValues = null;
+ } else {
+ localValues = new ArrayList<byte[]>();
+ int numValues = in.readInt();
+ for (int i = 0; i < numValues; i++) {
+ len = in.readInt();
+ byte val[] = new byte[len];
+ in.readFully(val);
+ localValues.add(val);
+ }
+ }
+
+ // convert data to new format
+ SimpleReader din = new SimpleReader(localData);
+ buffer = new ByteBuffer();
+ for (int i = 0; i < localEntries; i++) {
+ byte[] cf = oldReadBytes(din);
+ byte[] cq = oldReadBytes(din);
+ byte[] cv = oldReadBytes(din);
+ boolean hasts = din.readBoolean();
+ long ts = din.readLong();
+ boolean deleted = din.readBoolean();
+
+ byte[] val;
+ int valLen = din.readInt();
+
+ if (valLen < 0) {
+ val = localValues.get((-1 * valLen) - 1);
+ } else if (valLen == 0) {
+ val = EMPTY_BYTES;
+ } else {
+ val = new byte[valLen];
+ din.readBytes(val);
+ }
+
+ put(cf, cq, cv, hasts, ts, deleted, val);
+ if (!hasts)
+ droppingOldTimestamp(ts);
+ }
+
+ serialize();
+
+ }
+
+ @Override
+ public void write(DataOutput out) throws IOException {
+ serialize();
+ byte hasValues = (values == null) ? 0 : (byte)1;
+ out.write((byte)(0x80 | hasValues));
+
+ WritableUtils.writeVInt(out, row.length);
+ out.write(row);
+
+ WritableUtils.writeVInt(out, data.length);
+ out.write(data);
+ WritableUtils.writeVInt(out, entries);
+
+ if (hasValues > 0) {
+ WritableUtils.writeVInt(out, values.size());
+ for (int i = 0; i < values.size(); i++) {
+ byte val[] = values.get(i);
+ WritableUtils.writeVInt(out, val.length);
+ out.write(val);
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Mutation)
+ return equals((Mutation) o);
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return toThrift().hashCode();
+ }
+
+ public boolean equals(Mutation m) {
+ serialize();
++ m.serialize();
+ if (Arrays.equals(row, m.row) && entries == m.entries && Arrays.equals(data, m.data)) {
+ if (values == null && m.values == null)
+ return true;
+
+ if (values != null && m.values != null && values.size() == m.values.size()) {
+ for (int i = 0; i < values.size(); i++) {
+ if (!Arrays.equals(values.get(i), m.values.get(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ }
+
+ return false;
+ }
+
+ public TMutation toThrift() {
+ serialize();
+ return new TMutation(java.nio.ByteBuffer.wrap(row), java.nio.ByteBuffer.wrap(data), ByteBufferUtil.toByteBuffers(values), entries);
+ }
+
+ protected SERIALIZED_FORMAT getSerializedFormat() {
+ return this.useOldDeserialize ? SERIALIZED_FORMAT.VERSION1 : SERIALIZED_FORMAT.VERSION2;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/accumulo/blob/6eddb392/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
index 30acf85,0000000..09026f7
mode 100644,000000..100644
--- a/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/data/MutationTest.java
@@@ -1,511 -1,0 +1,579 @@@
+/*
+ * 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.accumulo.core.data;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.accumulo.core.security.ColumnVisibility;
+import org.apache.hadoop.io.Text;
+
+public class MutationTest extends TestCase {
+
+ private static String toHexString(byte[] ba) {
+ StringBuilder str = new StringBuilder();
+ for (int i = 0; i < ba.length; i++) {
+ str.append(String.format("%x", ba[i]));
+ }
+ return str.toString();
+ }
+
+ /* Test constructing a Mutation using a byte buffer. The byte array
+ * returned as the row is converted to a hexadecimal string for easy
+ * comparision.
+ */
+ public void testByteConstructor() {
+ Mutation m = new Mutation("0123456789".getBytes());
+ assertEquals("30313233343536373839", toHexString(m.getRow()));
+ }
+
+ public void testLimitedByteConstructor() {
+ Mutation m = new Mutation("0123456789".getBytes(), 2, 5);
+ assertEquals("3233343536", toHexString(m.getRow()));
+ }
+
+ public void test1() {
+ Mutation m = new Mutation(new Text("r1"));
+ m.put(new Text("cf1"), new Text("cq1"), new Value("v1".getBytes()));
+
+ List<ColumnUpdate> updates = m.getUpdates();
+
+ assertEquals(1, updates.size());
+
+ ColumnUpdate cu = updates.get(0);
+
+ assertEquals("cf1", new String(cu.getColumnFamily()));
+ assertEquals("cq1", new String(cu.getColumnQualifier()));
+ assertEquals("", new String(cu.getColumnVisibility()));
+ assertFalse(cu.hasTimestamp());
+
+ }
+
+ public void test2() throws IOException {
+ Mutation m = new Mutation(new Text("r1"));
+ m.put(new Text("cf1"), new Text("cq1"), new Value("v1".getBytes()));
+ m.put(new Text("cf2"), new Text("cq2"), 56, new Value("v2".getBytes()));
+
+ List<ColumnUpdate> updates = m.getUpdates();
+
+ assertEquals(2, updates.size());
+
+ assertEquals("r1", new String(m.getRow()));
+ ColumnUpdate cu = updates.get(0);
+
+ assertEquals("cf1", new String(cu.getColumnFamily()));
+ assertEquals("cq1", new String(cu.getColumnQualifier()));
+ assertEquals("", new String(cu.getColumnVisibility()));
+ assertFalse(cu.hasTimestamp());
+
+ cu = updates.get(1);
+
+ assertEquals("cf2", new String(cu.getColumnFamily()));
+ assertEquals("cq2", new String(cu.getColumnQualifier()));
+ assertEquals("", new String(cu.getColumnVisibility()));
+ assertTrue(cu.hasTimestamp());
+ assertEquals(56, cu.getTimestamp());
+
+ m = cloneMutation(m);
+
+ assertEquals("r1", new String(m.getRow()));
+ updates = m.getUpdates();
+
+ assertEquals(2, updates.size());
+
+ cu = updates.get(0);
+
+ assertEquals("cf1", new String(cu.getColumnFamily()));
+ assertEquals("cq1", new String(cu.getColumnQualifier()));
+ assertEquals("", new String(cu.getColumnVisibility()));
+ assertFalse(cu.hasTimestamp());
+
+ cu = updates.get(1);
+
+ assertEquals("cf2", new String(cu.getColumnFamily()));
+ assertEquals("cq2", new String(cu.getColumnQualifier()));
+ assertEquals("", new String(cu.getColumnVisibility()));
+ assertTrue(cu.hasTimestamp());
+ assertEquals(56, cu.getTimestamp());
+
+ }
+
+ private Mutation cloneMutation(Mutation m) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ m.write(dos);
+ dos.close();
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ DataInputStream dis = new DataInputStream(bais);
+
+ m = new Mutation();
+ m.readFields(dis);
+ return m;
+ }
+
+ public void test3() throws IOException {
+ Mutation m = new Mutation(new Text("r1"));
+ for (int i = 0; i < 5; i++) {
+ int len = Mutation.VALUE_SIZE_COPY_CUTOFF - 2 + i;
+ byte val[] = new byte[len];
+ for (int j = 0; j < len; j++)
+ val[j] = (byte) i;
+
+ m.put(new Text("cf" + i), new Text("cq" + i), new Value(val));
+
+ }
+
+ for (int r = 0; r < 3; r++) {
+ assertEquals("r1", new String(m.getRow()));
+ List<ColumnUpdate> updates = m.getUpdates();
+ assertEquals(5, updates.size());
+ for (int i = 0; i < 5; i++) {
+ ColumnUpdate cu = updates.get(i);
+ assertEquals("cf" + i, new String(cu.getColumnFamily()));
+ assertEquals("cq" + i, new String(cu.getColumnQualifier()));
+ assertEquals("", new String(cu.getColumnVisibility()));
+ assertFalse(cu.hasTimestamp());
+
+ byte[] val = cu.getValue();
+ int len = Mutation.VALUE_SIZE_COPY_CUTOFF - 2 + i;
+ assertEquals(len, val.length);
+ for (int j = 0; j < len; j++)
+ assertEquals(i, val[j]);
+ }
+
+ m = cloneMutation(m);
+ }
+ }
+
+ private Text nt(String s) {
+ return new Text(s);
+ }
+
+ private Value nv(String s) {
+ return new Value(s.getBytes());
+ }
+
+ public void testPuts() {
+ Mutation m = new Mutation(new Text("r1"));
+
+ m.put(nt("cf1"), nt("cq1"), nv("v1"));
+ m.put(nt("cf2"), nt("cq2"), new ColumnVisibility("cv2"), nv("v2"));
+ m.put(nt("cf3"), nt("cq3"), 3l, nv("v3"));
+ m.put(nt("cf4"), nt("cq4"), new ColumnVisibility("cv4"), 4l, nv("v4"));
+
+ m.putDelete(nt("cf5"), nt("cq5"));
+ m.putDelete(nt("cf6"), nt("cq6"), new ColumnVisibility("cv6"));
+ m.putDelete(nt("cf7"), nt("cq7"), 7l);
+ m.putDelete(nt("cf8"), nt("cq8"), new ColumnVisibility("cv8"), 8l);
+
+ assertEquals(8, m.size());
+
+ List<ColumnUpdate> updates = m.getUpdates();
+
+ assertEquals(8, m.size());
+ assertEquals(8, updates.size());
+
+ assertEquals(updates.get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(updates.get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ assertEquals(updates.get(2), "cf3", "cq3", "", 3l, true, false, "v3");
+ assertEquals(updates.get(3), "cf4", "cq4", "cv4", 4l, true, false, "v4");
+
+ assertEquals(updates.get(4), "cf5", "cq5", "", 0l, false, true, "");
+ assertEquals(updates.get(5), "cf6", "cq6", "cv6", 0l, false, true, "");
+ assertEquals(updates.get(6), "cf7", "cq7", "", 7l, true, true, "");
+ assertEquals(updates.get(7), "cf8", "cq8", "cv8", 8l, true, true, "");
+
+ }
+
+ public void testPutsString() {
+ Mutation m = new Mutation("r1");
+
+ m.put("cf1", "cq1", nv("v1"));
+ m.put("cf2", "cq2", new ColumnVisibility("cv2"), nv("v2"));
+ m.put("cf3", "cq3", 3l, nv("v3"));
+ m.put("cf4", "cq4", new ColumnVisibility("cv4"), 4l, nv("v4"));
+
+ m.putDelete("cf5", "cq5");
+ m.putDelete("cf6", "cq6", new ColumnVisibility("cv6"));
+ m.putDelete("cf7", "cq7", 7l);
+ m.putDelete("cf8", "cq8", new ColumnVisibility("cv8"), 8l);
+
+ assertEquals(8, m.size());
+
+ List<ColumnUpdate> updates = m.getUpdates();
+
+ assertEquals(8, m.size());
+ assertEquals(8, updates.size());
+
+ assertEquals(updates.get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(updates.get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ assertEquals(updates.get(2), "cf3", "cq3", "", 3l, true, false, "v3");
+ assertEquals(updates.get(3), "cf4", "cq4", "cv4", 4l, true, false, "v4");
+
+ assertEquals(updates.get(4), "cf5", "cq5", "", 0l, false, true, "");
+ assertEquals(updates.get(5), "cf6", "cq6", "cv6", 0l, false, true, "");
+ assertEquals(updates.get(6), "cf7", "cq7", "", 7l, true, true, "");
+ assertEquals(updates.get(7), "cf8", "cq8", "cv8", 8l, true, true, "");
+ }
+
+ public void testPutsStringString() {
+ Mutation m = new Mutation("r1");
+
+ m.put("cf1", "cq1", "v1");
+ m.put("cf2", "cq2", new ColumnVisibility("cv2"), "v2");
+ m.put("cf3", "cq3", 3l, "v3");
+ m.put("cf4", "cq4", new ColumnVisibility("cv4"), 4l, "v4");
+
+ m.putDelete("cf5", "cq5");
+ m.putDelete("cf6", "cq6", new ColumnVisibility("cv6"));
+ m.putDelete("cf7", "cq7", 7l);
+ m.putDelete("cf8", "cq8", new ColumnVisibility("cv8"), 8l);
+
+ assertEquals(8, m.size());
+ assertEquals("r1", new String(m.getRow()));
+
+ List<ColumnUpdate> updates = m.getUpdates();
+
+ assertEquals(8, m.size());
+ assertEquals(8, updates.size());
+
+ assertEquals(updates.get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(updates.get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ assertEquals(updates.get(2), "cf3", "cq3", "", 3l, true, false, "v3");
+ assertEquals(updates.get(3), "cf4", "cq4", "cv4", 4l, true, false, "v4");
+
+ assertEquals(updates.get(4), "cf5", "cq5", "", 0l, false, true, "");
+ assertEquals(updates.get(5), "cf6", "cq6", "cv6", 0l, false, true, "");
+ assertEquals(updates.get(6), "cf7", "cq7", "", 7l, true, true, "");
+ assertEquals(updates.get(7), "cf8", "cq8", "cv8", 8l, true, true, "");
+ }
+
+ public void testByteArrays() {
+ Mutation m = new Mutation("r1".getBytes());
+
+ m.put("cf1".getBytes(), "cq1".getBytes(), "v1".getBytes());
+ m.put("cf2".getBytes(), "cq2".getBytes(), new ColumnVisibility("cv2"), "v2".getBytes());
+ m.put("cf3".getBytes(), "cq3".getBytes(), 3l, "v3".getBytes());
+ m.put("cf4".getBytes(), "cq4".getBytes(), new ColumnVisibility("cv4"), 4l, "v4".getBytes());
+
+ m.putDelete("cf5".getBytes(), "cq5".getBytes());
+ m.putDelete("cf6".getBytes(), "cq6".getBytes(), new ColumnVisibility("cv6"));
+ m.putDelete("cf7".getBytes(), "cq7".getBytes(), 7l);
+ m.putDelete("cf8".getBytes(), "cq8".getBytes(), new ColumnVisibility("cv8"), 8l);
+
+ assertEquals(8, m.size());
+
+ List<ColumnUpdate> updates = m.getUpdates();
+
+ assertEquals(8, m.size());
+ assertEquals(8, updates.size());
+
+ assertEquals(updates.get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(updates.get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ assertEquals(updates.get(2), "cf3", "cq3", "", 3l, true, false, "v3");
+ assertEquals(updates.get(3), "cf4", "cq4", "cv4", 4l, true, false, "v4");
+
+ assertEquals(updates.get(4), "cf5", "cq5", "", 0l, false, true, "");
+ assertEquals(updates.get(5), "cf6", "cq6", "cv6", 0l, false, true, "");
+ assertEquals(updates.get(6), "cf7", "cq7", "", 7l, true, true, "");
+ assertEquals(updates.get(7), "cf8", "cq8", "cv8", 8l, true, true, "");
+ }
+
+ /**
+ * Test for regression on bug 3422. If a {@link Mutation} object is reused for multiple calls to readFields, the mutation would previously be "locked in" to
+ * the first set of column updates (and value lengths). Hadoop input formats reuse objects when reading, so if Mutations are used with an input format (or as
+ * the input to a combiner or reducer) then they will be used in this fashion.
+ */
+ public void testMultipleReadFieldsCalls() throws IOException {
+ // Create test mutations and write them to a byte output stream
+ Mutation m1 = new Mutation("row1");
+ m1.put("cf1.1", "cq1.1", new ColumnVisibility("A|B"), "val1.1");
+ m1.put("cf1.2", "cq1.2", new ColumnVisibility("C|D"), "val1.2");
+ byte[] val1_3 = new byte[Mutation.VALUE_SIZE_COPY_CUTOFF + 3];
+ Arrays.fill(val1_3, (byte) 3);
+ m1.put("cf1.3", "cq1.3", new ColumnVisibility("E|F"), new String(val1_3));
+ int size1 = m1.size();
+ long nb1 = m1.numBytes();
+
+ Mutation m2 = new Mutation("row2");
+ byte[] val2 = new byte[Mutation.VALUE_SIZE_COPY_CUTOFF + 2];
+ Arrays.fill(val2, (byte) 2);
+ m2.put("cf2", "cq2", new ColumnVisibility("G|H"), 1234, new String(val2));
+ int size2 = m2.size();
+ long nb2 = m2.numBytes();
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ m1.write(dos);
+ m2.write(dos);
+ dos.close();
+
+ // Now read the mutations back in from the byte array, making sure to
+ // reuse the same mutation object, and make sure everything is correct.
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ DataInputStream dis = new DataInputStream(bis);
+
+ Mutation m = new Mutation();
+ m.readFields(dis);
+
+ assertEquals("row1", new String(m.getRow()));
+ assertEquals(size1, m.size());
+ assertEquals(nb1, m.numBytes());
+ assertEquals(3, m.getUpdates().size());
+ assertEquals(m.getUpdates().get(0), "cf1.1", "cq1.1", "A|B", 0L, false, false, "val1.1");
+ assertEquals(m.getUpdates().get(1), "cf1.2", "cq1.2", "C|D", 0L, false, false, "val1.2");
+ assertEquals(m.getUpdates().get(2), "cf1.3", "cq1.3", "E|F", 0L, false, false, new String(val1_3));
+
+ // Reuse the same mutation object (which is what happens in the hadoop framework
+ // when objects are read by an input format)
+ m.readFields(dis);
+
+ assertEquals("row2", new String(m.getRow()));
+ assertEquals(size2, m.size());
+ assertEquals(nb2, m.numBytes());
+ assertEquals(1, m.getUpdates().size());
+ assertEquals(m.getUpdates().get(0), "cf2", "cq2", "G|H", 1234L, true, false, new String(val2));
+ }
+
+ private void assertEquals(ColumnUpdate cu, String cf, String cq, String cv, long ts, boolean timeSet, boolean deleted, String val) {
+
+ assertEquals(cf, new String(cu.getColumnFamily()));
+ assertEquals(cq, new String(cu.getColumnQualifier()));
+ assertEquals(cv, new String(cu.getColumnVisibility()));
+ assertEquals(timeSet, cu.hasTimestamp());
+ if (timeSet)
+ assertEquals(ts, cu.getTimestamp());
+ assertEquals(deleted, cu.isDeleted());
+ assertEquals(val, new String(cu.getValue()));
+ }
+
+ public void test4() throws Exception {
+ Mutation m1 = new Mutation(new Text("r1"));
+
+ m1.put(nt("cf1"), nt("cq1"), nv("v1"));
+ m1.put(nt("cf2"), nt("cq2"), new ColumnVisibility("cv2"), nv("v2"));
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ m1.write(dos);
+ dos.close();
+
+ Mutation m2 = new Mutation(new Text("r2"));
+
+ m2.put(nt("cf3"), nt("cq3"), nv("v3"));
+ m2.put(nt("cf4"), nt("cq4"), new ColumnVisibility("cv2"), nv("v4"));
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ DataInputStream dis = new DataInputStream(bis);
+
+ // used to be a bug where puts done before readFields would be seen
+ // after readFields
+ m2.readFields(dis);
+
+ assertEquals("r1", new String(m2.getRow()));
+ assertEquals(2, m2.getUpdates().size());
+ assertEquals(2, m2.size());
+ assertEquals(m2.getUpdates().get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(m2.getUpdates().get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ }
+
+ Mutation convert(OldMutation old) throws IOException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ old.write(dos);
+ dos.close();
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ DataInputStream dis = new DataInputStream(bis);
+ Mutation m = new Mutation();
+ m.readFields(dis);
+ dis.close();
+ return m;
+ }
+
+
+ public void testNewSerialization() throws Exception {
+ // write an old mutation
+ OldMutation m2 = new OldMutation("r1");
+ m2.put("cf1", "cq1", "v1");
+ m2.put("cf2", "cq2", new ColumnVisibility("cv2"), "v2");
+ m2.putDelete("cf3", "cq3");
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ m2.write(dos);
+ dos.close();
+ long oldSize = dos.size();
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ DataInputStream dis = new DataInputStream(bis);
+ m2.readFields(dis);
+ dis.close();
+
+ // check it
+ assertEquals("r1", new String(m2.getRow()));
+ assertEquals(3, m2.getUpdates().size());
+ assertEquals(3, m2.size());
+ assertEquals(m2.getUpdates().get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(m2.getUpdates().get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ assertEquals(m2.getUpdates().get(2), "cf3", "cq3", "", 0l, false, true, "");
+
+ Mutation m1 = convert(m2);
+
+ assertEquals("r1", new String(m1.getRow()));
+ assertEquals(3, m1.getUpdates().size());
+ assertEquals(3, m1.size());
+ assertEquals(m1.getUpdates().get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(m1.getUpdates().get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ assertEquals(m1.getUpdates().get(2), "cf3", "cq3", "", 0l, false, true, "");
+
+ Text exampleRow = new Text(" 123456789 123456789 123456789 123456789 123456789");
+ int exampleLen = exampleRow.getLength();
+ m1 = new Mutation(exampleRow);
+ m1.put("", "", "");
+
+ bos = new ByteArrayOutputStream();
+ dos = new DataOutputStream(bos);
+ m1.write(dos);
+ dos.close();
+ long newSize = dos.size();
+ assertTrue(newSize < oldSize);
+ assertEquals(10, newSize - exampleLen);
+ assertEquals(68, oldSize - exampleLen);
+ // I am converting to integer to avoid comparing floats which are inaccurate
+ assertEquals(14705, (int)(((newSize-exampleLen) * 100. / (oldSize - exampleLen)) * 1000));
+ StringBuilder sb = new StringBuilder();
+ byte[] ba = bos.toByteArray();
+ for (int i = 0; i < bos.size(); i += 4) {
+ for (int j = i; j < bos.size() && j < i + 4; j++) {
+ sb.append(String.format("%02x", ba[j]));
+ }
+ sb.append(" ");
+ }
+ assertEquals("80322031 32333435 36373839 20313233 34353637 38392031 32333435 36373839 20313233 34353637 38392031 32333435 36373839 06000000 00000001 ", sb.toString());
+
+ }
+
+ public void testReserialize() throws Exception {
+ // test reading in a new mutation from an old mutation and reserializing the new mutation... this was failing
+ OldMutation om = new OldMutation("r1");
+ om.put("cf1", "cq1", "v1");
+ om.put("cf2", "cq2", new ColumnVisibility("cv2"), "v2");
+ om.putDelete("cf3", "cq3");
+ StringBuilder bigVal = new StringBuilder();
+ for (int i = 0; i < 100000; i++) {
+ bigVal.append('a');
+ }
+ om.put("cf2", "big", bigVal);
+
+
+ Mutation m1 = convert(om);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ m1.write(dos);
+ dos.close();
+
+ Mutation m2 = new Mutation();
+
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ DataInputStream dis = new DataInputStream(bis);
+ m2.readFields(dis);
+
+ assertEquals("r1", new String(m1.getRow()));
+ assertEquals(4, m2.getUpdates().size());
+ assertEquals(4, m2.size());
+ assertEquals(m2.getUpdates().get(0), "cf1", "cq1", "", 0l, false, false, "v1");
+ assertEquals(m2.getUpdates().get(1), "cf2", "cq2", "cv2", 0l, false, false, "v2");
+ assertEquals(m2.getUpdates().get(2), "cf3", "cq3", "", 0l, false, true, "");
+ assertEquals(m2.getUpdates().get(3), "cf2", "big", "", 0l, false, false, bigVal.toString());
+ }
+
++ public void testEquals() {
++ Mutation m1 = new Mutation("r1");
++
++ m1.put("cf1", "cq1", "v1");
++ m1.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
++ m1.put("cf1", "cq1", 3, "v3");
++ m1.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
++ m1.putDelete("cf2", "cf3");
++ m1.putDelete("cf2", "cf4", 3);
++ m1.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
++
++ // m2 has same data as m1
++ Mutation m2 = new Mutation("r1");
++
++ m2.put("cf1", "cq1", "v1");
++ m2.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
++ m2.put("cf1", "cq1", 3, "v3");
++ m2.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
++ m2.putDelete("cf2", "cf3");
++ m2.putDelete("cf2", "cf4", 3);
++ m2.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
++
++ // m3 has differnt row than m1
++ Mutation m3 = new Mutation("r2");
++
++ m3.put("cf1", "cq1", "v1");
++ m3.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
++ m3.put("cf1", "cq1", 3, "v3");
++ m3.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
++ m3.putDelete("cf2", "cf3");
++ m3.putDelete("cf2", "cf4", 3);
++ m3.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
++
++ // m4 has a different column than m1
++ Mutation m4 = new Mutation("r1");
++
++ m4.put("cf2", "cq1", "v1");
++ m4.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
++ m4.put("cf1", "cq1", 3, "v3");
++ m4.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
++ m4.putDelete("cf2", "cf3");
++ m4.putDelete("cf2", "cf4", 3);
++ m4.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
++
++ // m5 has a different value than m1
++ Mutation m5 = new Mutation("r1");
++
++ m5.put("cf1", "cq1", "v1");
++ m5.put("cf1", "cq1", new ColumnVisibility("A&B"), "v2");
++ m5.put("cf1", "cq1", 3, "v4");
++ m5.put("cf1", "cq1", new ColumnVisibility("A&B&C"), 4, "v4");
++ m5.putDelete("cf2", "cf3");
++ m5.putDelete("cf2", "cf4", 3);
++ m5.putDelete("cf2", "cf4", new ColumnVisibility("A&B&C"), 3);
++
++ assertEquals(m1, m1);
++ assertEquals(m1, m2);
++ assertEquals(m2, m1);
++ assertFalse(m1.equals(m3));
++ assertFalse(m3.equals(m1));
++ assertFalse(m1.equals(m4));
++ assertFalse(m4.equals(m1));
++ assertFalse(m3.equals(m4));
++ assertFalse(m1.equals(m5));
++ assertFalse(m5.equals(m1));
++ assertFalse(m3.equals(m5));
++ assertFalse(m4.equals(m5));
++ }
+}